Public Class form_Query
    Dim SectionNameList As New ArrayList
    Dim TableNameList As New ArrayList

    Dim m_ComboDataType(COLS.max - 1) As COMBO_DATA_TYPE   '存储下拉列表的类型
    Dim m_ConditionCount As Integer = 0
    Dim m_EditCol As Integer
    Dim m_EditRow As Integer

    Dim QueryTable As New DataTable

    Public Class SECTION_UNIT
        Public name As String
        Public check As Boolean
    End Class

    Enum COMBO_DATA_TYPE
        table
        section
    End Enum

    Enum COLS
        序号
        关联
        字段
        运算
        字段_值
        max
    End Enum

    Enum SECTION_TYPE
        无效
        值
        字段
    End Enum

    '获取W&T Database数据库中所有的表
    Private Function GetTableList() As Boolean
        Dim rTable As New Data.DataTable
        Dim result As Boolean = True

        TableNameList.Clear()
        rTable.Columns.Clear()
        rTable.Rows.Clear()
        rTable.Clear()
        If SQL_Query(CAccountManage.COL_RIGHTS.登录, "show tables", rTable) = True Then
            If rTable.Rows.Count > 0 Then
                Try
                    For i As Integer = 0 To rTable.Rows.Count - 1
                        Dim pSectionUnit As New SECTION_UNIT
                        pSectionUnit.name = rTable.Rows.Item(i).Item(0)
                        pSectionUnit.check = False
                        TableNameList.Add(pSectionUnit)
                    Next
                Catch ex As Exception

                End Try
            End If
        Else
            result = False
        End If

        Return result
    End Function

    '获取所有表对应的字段名称列表
    Private Function GetSectionNameList() As Boolean
        Dim rTable As New Data.DataTable
        Dim result As Boolean = True

        SectionNameList.Clear()
        Dim TableName As String
        For Each pTableInfo As SECTION_UNIT In TableNameList
            TableName = pTableInfo.name

            Dim pItemList As New ArrayList

            rTable.Columns.Clear()
            rTable.Rows.Clear()
            rTable.Clear()
            If SQL_Query("SELECT * FROM `" & TableName & "` WHERE 0", rTable) = True Then
                For i As Integer = 0 To rTable.Columns.Count - 1
                    Dim pSectionUnit As New SECTION_UNIT
                    pSectionUnit.name = rTable.Columns.Item(i).ColumnName
                    pSectionUnit.check = False
                    pItemList.Add(pSectionUnit)
                Next

                SectionNameList.Add(pItemList)
            Else
                result = False
            End If
        Next

        Return result
    End Function

    Private Sub form_Query_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If GetTableList() = False Then
            MsgBox("载入数据表错误")
            Me.Close()
            Return
        End If
        If GetSectionNameList() = False Then
            MsgBox("载入数据表字段错误")
            Me.Close()
            Return
        End If

        '填充查询表
        Fill_TableList()

        '初始化Conditions列表
        Init_ConditionTable()
    End Sub

    Private Sub Init_ConditionTable()
        Grid_Conditions.Cols = COLS.max
        Grid_Conditions.Rows = 2

        '填充列名
        Grid_Conditions.Cell(0, 0).Text = "序号"
        Grid_Conditions.Cell(0, 1).Text = "关联"
        Grid_Conditions.Cell(0, 2).Text = "字段"
        Grid_Conditions.Cell(0, 3).Text = "运算"
        Grid_Conditions.Cell(0, 4).Text = "字段/值"

        Grid_Conditions.Cell(1, 0).Text = m_ConditionCount + 1
        Grid_Conditions.Cell(COLS.关联, 1).Locked = True

        '填入“关联”字段列表值
        Grid_Conditions.Column(COLS.关联).CellType = FlexCell.CellTypeEnum.ComboBox
        Dim i As Integer
        With Grid_Conditions.ComboBox(COLS.关联)
            .Locked = True
            .AutoComplete = True
            For i = 0 To mdl_SQLConst.rel_Conditions.Length / 2 - 1
                .Items.Add(mdl_SQLConst.rel_Conditions(i, 0))
            Next
        End With

        '填入“运算”字段列表值
        Grid_Conditions.Column(COLS.运算).CellType = FlexCell.CellTypeEnum.ComboBox
        With Grid_Conditions.ComboBox(COLS.运算)
            .Locked = True
            .AutoComplete = True
            For i = 0 To mdl_SQLConst.rel_Operator.Length / 2 - 1
                .Items.Add(mdl_SQLConst.rel_Operator(i, 0))
            Next
        End With

        '填入“字段”
        Grid_Conditions.Column(COLS.字段).Width = 80
        Grid_Conditions.Column(COLS.字段).CellType = FlexCell.CellTypeEnum.ComboBox
        Fill_ComboList(Grid_Conditions.ComboBox(COLS.字段), "", 0, COMBO_DATA_TYPE.table)

        '填入"字段/值"
        Grid_Conditions.Column(COLS.字段_值).Width = 80
        Grid_Conditions.Column(COLS.字段_值).CellType = FlexCell.CellTypeEnum.ComboBox
        Fill_ComboList(Grid_Conditions.ComboBox(COLS.字段_值), "", 0, COMBO_DATA_TYPE.table)
    End Sub

    Private Sub Fill_TableList()
        clb_Table.Items.Clear()

        For Each pItem As SECTION_UNIT In TableNameList
            clb_Table.Items.Add(pItem.name)
        Next
    End Sub

    Private Function Fill_SectionList(ByVal index As Integer) As Boolean
        If index > SectionNameList.Count Then Return False

        Dim pItem As ArrayList = SectionNameList.Item(index)
        clb_Section.Items.Clear()

        Dim sec_idx As Integer
        For Each pSec As SECTION_UNIT In pItem
            sec_idx = clb_Section.Items.Add(pSec.name)
            clb_Section.SetItemChecked(sec_idx, pSec.check)
        Next

        Return True
    End Function

    Private Sub chklb_Table_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles clb_Table.SelectedIndexChanged
        If clb_Table.SelectedIndex >= 0 Then
            Fill_SectionList(clb_Table.SelectedIndex)
        End If
    End Sub


    Private Function GetAnySectionChecked(ByVal currIndex As Integer) As Boolean
        Dim checked As Boolean = False
        For i As Integer = 0 To clb_Section.Items.Count - 1
            If i <> currIndex Then
                If clb_Section.GetItemChecked(i) = True Then
                    checked = True
                    Exit For
                End If
            End If
        Next

        Return checked
    End Function

    Private Sub clb_Table_ItemCheck(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles clb_Table.ItemCheck
        Dim pTable As SECTION_UNIT = TableNameList.Item(e.Index)
        pTable.check = e.NewValue
        SQLGenerateFromCondition()
    End Sub

    Private Sub clb_Section_ItemCheck(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles clb_Section.ItemCheck
        If clb_Table.SelectedIndex >= 0 AndAlso e.Index >= 0 Then
            Dim pTable As ArrayList = SectionNameList(clb_Table.SelectedIndex)
            Dim pSec As SECTION_UNIT = pTable(e.Index)
            pSec.check = e.NewValue

            If e.NewValue OrElse GetAnySectionChecked(e.Index) = True Then
                clb_Table.SetItemChecked(clb_Table.SelectedIndex, True)
            Else
                clb_Table.SetItemChecked(clb_Table.SelectedIndex, False)
            End If

            SQLGenerateFromCondition()
        End If
    End Sub

    Private Sub Grid_Conditions_LeaveCell(ByVal Sender As System.Object, ByVal e As FlexCell.Grid.LeaveCellEventArgs) Handles Grid_Conditions.LeaveCell
        SQLGenerateFromCondition()
    End Sub

    '填充dgview_Condition中的“字段”下拉框列表
    Private Function Fill_ComboList(ByRef pCombo As FlexCell.ComboBox, ByVal PreTableName As String, ByVal index As Integer, ByVal type As COMBO_DATA_TYPE) As Boolean
        Dim pSection As SECTION_UNIT

        pCombo.Items.Clear()
        pCombo.DropDownWidth = 200
        If type = COMBO_DATA_TYPE.table Then
            For i As Integer = 0 To TableNameList.Count - 1
                pSection = TableNameList(i)
                If pSection.check = True Then
                    pCombo.Items.Add(pSection.name)
                End If
            Next
        Else
            If index > SectionNameList.Count Then Return False

            Dim pTable As ArrayList = SectionNameList.Item(index)

            For Each pSec As SECTION_UNIT In pTable
                pCombo.Items.Add(PreTableName & "." & pSec.name)
            Next
        End If

        Return True
    End Function

    Private Sub UpdateComboBoxList(ByVal row As Integer, ByVal col As Integer)
        'Dim col As Integer = e.Col
        'Dim row As Integer = e.Row

        If col < 1 OrElse row < 1 Then Return

        If col = COLS.字段 OrElse col = COLS.字段_值 Then
            Try
                Dim pCombo As FlexCell.ComboBox = Grid_Conditions.ComboBox(col)

                Dim strContent As String = Grid_Conditions.Cell(row, col).Text
                Dim dotPos As Integer = Strings.InStr(strContent, ".")

                If dotPos > 0 Then
                    Dim tablename As String = Strings.Left(strContent, dotPos - 1)
                    Dim index As Integer = GetTableIndex(tablename)
                    If index >= 0 Then
                        If pCombo.Items.Count = 0 OrElse m_ComboDataType(col) <> COMBO_DATA_TYPE.section Then
                            Fill_ComboList(pCombo, tablename, index, COMBO_DATA_TYPE.section)
                            m_ComboDataType(col) = COMBO_DATA_TYPE.section
                        End If
                    End If
                Else
                    If pCombo.Items.Count = 0 OrElse m_ComboDataType(col) <> COMBO_DATA_TYPE.table Then
                        Fill_ComboList(pCombo, "", 0, COMBO_DATA_TYPE.table)
                        m_ComboDataType(col) = COMBO_DATA_TYPE.table
                    End If
                End If

                pCombo.DropDown()
            Catch ex As Exception

            End Try
        End If
    End Sub


    'Private Sub Grid_Conditions_ComboClick(ByVal Sender As System.Object, ByVal e As FlexCell.Grid.ComboClickEventArgs) Handles Grid_Conditions.ComboClick
    '    '更新下拉列表
    '    UpdateComboBoxList(m_EditRow, m_EditCol)
    'End Sub

    Private Sub Grid_Conditions_CellChanging(ByVal Sender As System.Object, ByVal e As FlexCell.Grid.CellChangingEventArgs) Handles Grid_Conditions.CellChanging
        '更新下拉列表
        UpdateComboBoxList(e.Row, e.Col)

        '自动添加"序号"
        If Grid_Conditions.Cell(e.Row, 0).Text <> e.Row.ToString Then
            Grid_Conditions.Cell(e.Row, 0).Text = e.Row
        End If

        '自动增加一行
        If Grid_Conditions.Rows = (e.Row + 1) Then
            Grid_Conditions.Rows += 1
        End If
    End Sub

    Private Function dgview_Condition() As Object
        Throw New NotImplementedException
    End Function

    'Private Sub Grid_Conditions_EditRow(ByVal Sender As System.Object, ByVal e As FlexCell.Grid.EditRowEventArgs) Handles Grid_Conditions.EditRow
    '    If Grid_Conditions.Cell(e.Row, 0).Text <> e.Row.ToString Then
    '        Grid_Conditions.Cell(e.Row, 0).Text = e.Row
    '    End If
    'End Sub

    Private Sub Grid_Conditions_RowColChange(ByVal Sender As System.Object, ByVal e As FlexCell.Grid.RowColChangeEventArgs) Handles Grid_Conditions.RowColChange
        If e.Col = COLS.字段 OrElse e.Col = COLS.字段_值 Then
            bt_FillIn.Enabled = True
        Else
            bt_FillIn.Enabled = False
        End If

        m_EditCol = e.Col
        m_EditRow = e.Row

        '更新下拉列表
        UpdateComboBoxList(m_EditRow, m_EditCol)
    End Sub

    Private Sub bt_FillIn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_FillIn.Click
        Dim table_idx As Integer = clb_Table.SelectedIndex
        Dim section_idx As Integer = clb_Section.SelectedIndex

        If table_idx >= 0 AndAlso section_idx >= 0 Then
            Grid_Conditions.Cell(m_EditRow, m_EditCol).Text = clb_Table.Items(table_idx) & "." & clb_Section.Items(section_idx)
        End If
    End Sub

    '在TableNameList中查找tblName，并返回序号
    '如果未找到则返回-1
    Private Function GetTableIndex(ByVal tblName As String) As Integer
        Dim index As Integer = 0
        For Each pTable As SECTION_UNIT In TableNameList
            If pTable.name.ToUpper = tblName.ToUpper Then
                Return index
            End If
            index += 1
        Next

        Return -1
    End Function

    '在SectionNameList中查找secName，并返回序号
    '如果未找到则返回-1
    Private Function GetSectionIndex(ByVal tblIndex As Integer, ByVal secName As String) As Integer
        Dim pTable As ArrayList = SectionNameList.Item(tblIndex)

        Dim index As Integer = 0
        For Each pSec As SECTION_UNIT In pTable
            If pSec.name.ToUpper = secName.ToUpper Then
                Return index
            End If
            index += 1
        Next

        Return -1
    End Function

    '检查指定的行是否为空
    Private Function CheckConditionTableLineEmpty(ByVal row As Integer) As Boolean
        Dim line_not_empty As Boolean = False
        Dim cell_text As String
        For col = 1 To Grid_Conditions.Cols - 1
            If Not (col = COLS.关联 And row = 1) Then '忽略行1的关联单元
                Try
                    cell_text = Grid_Conditions.Cell(row, col).Text
                    If cell_text.Length > 0 Then
                        line_not_empty = True
                        Exit For
                    End If
                Catch ex As Exception

                End Try
            End If
        Next
        Return line_not_empty
    End Function

    '检查指定字符串是否为有效的字段名
    Private Function CheckTextIsValidSectionName(ByVal text As String) As SECTION_TYPE
        If text.Length = 0 Then Return SECTION_TYPE.无效

        Dim m_section_type As SECTION_TYPE = SECTION_TYPE.无效
        Dim cell_split() As String = Split(text, ".")
        If cell_split.Length >= 2 Then  '如果包含点，则检查是否为有效的字段名
            '查找当前表名是否存在
            Dim tbl_index As Integer = GetTableIndex(cell_split(0))
            If tbl_index >= 0 Then
                If GetSectionIndex(tbl_index, cell_split(1)) >= 0 Then
                    m_section_type = SECTION_TYPE.字段
                End If
            End If
        Else
            '否则为其它值,非字段名
            m_section_type = SECTION_TYPE.值
        End If

        Return m_section_type
    End Function

    '对Grid_Conditions列表进行错误检查
    '并将错误的单元标记为红色
    '返回   True -- 无错误
    '       False -- 错误
    Private Function ConditionCheck() As Boolean
        Dim col As Integer
        Dim row As Integer
        Dim col_count As Integer = Grid_Conditions.Cols
        Dim row_count As Integer = Grid_Conditions.Rows
        Dim cell_text As String

        Dim valid As Boolean = True
        Dim line_not_empty As Boolean
        Dim line_valid As Boolean
        Dim cell_valid As Boolean
        For row = 1 To row_count - 1
            line_valid = True
            '检查当前行是否为空行
            line_not_empty = CheckConditionTableLineEmpty(row)

            '如果当前行不为空
            If line_not_empty = True Then
                '检测每个行中的值是否有效
                For col = 0 To col_count - 1
                    cell_valid = True
                    If Not (col = COLS.关联 And row = 1) Then '忽略行1的关联单元
                        cell_text = Grid_Conditions.Cell(row, col).Text
                        '当前单元不能为空
                        If cell_text.Length > 0 Then
                            '字段列中的表与字段需要有效
                            If col = COLS.字段 OrElse col = COLS.字段_值 Then
                                If CheckTextIsValidSectionName(cell_text) = SECTION_TYPE.无效 Then
                                    cell_valid = False
                                End If
                            End If
                        Else
                            cell_valid = False
                        End If
                    End If

                    If cell_valid = False Then
                        Grid_Conditions.Cell(row, col).BackColor = Color.Red
                        line_valid = False
                    Else
                        Grid_Conditions.Cell(row, col).BackColor = Color.White
                    End If
                Next
            End If

            If line_valid = False Then
                Grid_Conditions.Cell(row, 0).BackColor = Color.Red
                valid = False
            Else
                Grid_Conditions.Cell(row, 0).BackColor = Grid_Conditions.Cell(0, 0).BackColor
            End If
        Next

        Return valid
    End Function

    '依据条件设置，生成对应的SQL语句
    Private Sub SQLGenerateFromCondition()
        Dim strSQL_all As String = ""
        Dim strSQL_Sections As String = ""
        Dim strSQL_Tables As String = ""
        Dim strSQL_Conditions As String = ""

        Dim pTable As SECTION_UNIT
        Dim pSecList As ArrayList
        Dim pSec As SECTION_UNIT
        Dim tbl_idx As Integer
        Dim sec_idx As Integer

        '查询字段与表
        For tbl_idx = 0 To TableNameList.Count - 1
            pTable = TableNameList.Item(tbl_idx)

            '如果表被选中
            If pTable.check = True Then
                If strSQL_Tables.Length > 0 Then
                    strSQL_Tables &= ","
                End If
                strSQL_Tables &= "`" & pTable.name & "`"

                pSecList = SectionNameList.Item(tbl_idx)
                For sec_idx = 0 To pSecList.Count - 1
                    pSec = pSecList.Item(sec_idx)
                    If pSec.check = True Then
                        If strSQL_Sections.Length > 0 Then
                            strSQL_Sections &= ","
                        End If
                        strSQL_Sections &= "`" & pTable.name & "`.`" & pSec.name & "`"
                    End If
                Next
            End If
        Next

        '查询条件 
        'Dim col As Integer
        Dim row As Integer
        Dim col_count As Integer = Grid_Conditions.Cols
        Dim row_count As Integer = Grid_Conditions.Rows
        Dim cell_text As String

        Dim valid As Boolean = True
        Dim line_not_empty As Boolean
        Dim line_valid As Boolean
        For row = 1 To row_count - 1
            line_valid = True
            '检查当前行是否为空行
            line_not_empty = CheckConditionTableLineEmpty(row)

            If line_not_empty = True Then
                If strSQL_Conditions.Length > 0 Then
                    strSQL_Conditions &= " "
                End If

                '关联
                cell_text = Grid_Conditions.Cell(row, COLS.关联).Text
                If cell_text.Length > 0 Then
                    strSQL_Conditions &= GetRelConditionSQL(cell_text) & " "
                End If

                '字段
                cell_text = Grid_Conditions.Cell(row, COLS.字段).Text
                strSQL_Conditions &= cell_text

                '运算符
                cell_text = Grid_Conditions.Cell(row, COLS.运算).Text
                strSQL_Conditions &= " " & GetRelOperatorSQL(cell_text) & " "

                '字段/值
                cell_text = Grid_Conditions.Cell(row, COLS.字段_值).Text
                If CheckTextIsValidSectionName(cell_text) = SECTION_TYPE.字段 Then
                    strSQL_Conditions &= " " & cell_text & " "
                Else
                    strSQL_Conditions &= "'" & cell_text & "' "
                End If
            End If
        Next

        '合并各个字段
        strSQL_all = "SELECT " & strSQL_Sections & " FROM " & strSQL_Tables
        If strSQL_Conditions.Length > 0 Then
            strSQL_all &= " WHERE " & strSQL_Conditions
        End If

        txt_SQL.Text = strSQL_all
    End Sub

    '通过“关联”名称获取对应的SQL表示符
    Private Function GetRelConditionSQL(ByVal disname As String) As String
        For i As Integer = 0 To rel_Conditions.Length / 2 - 1
            If rel_Conditions(i, 0) = disname Then
                Return rel_Conditions(i, 1)
            End If
        Next
        Return ""
    End Function

    '通过"运算符"民初获取对应的SQL表示符
    Private Function GetRelOperatorSQL(ByVal disname As String) As String
        For i As Integer = 0 To rel_Operator.Length / 2 - 1
            If rel_Operator(i, 0) = disname Then
                Return rel_Operator(i, 1)
            End If
        Next
        Return ""
    End Function

    Private Sub bt_Query_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_Query.Click
        If ConditionCheck() = False Then
            MsgBox("条件列表存在错误!")
        Else
            QueryTable.Columns.Clear()
            QueryTable.Rows.Clear()
            QueryTable.Clear()
            If SQL_Query(txt_SQL.Text, QueryTable) = True Then
                grid_Result.SetDataBinding(QueryTable)
            End If
            grid_Result.Locked = True

            lblItemCount.Text = "共 " & QueryTable.Rows.Count & " 条"
        End If
    End Sub

End Class