using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using NLog;
using System.Data.SqlClient;    // SqlCommand
using System.Data;              // DataTable

namespace ProcessDatabase
{
    public class Process
    {
        Logger log = LogManager.GetLogger("agent_logger");
        
        // process select query
        public DataSet ProcessSelect(String[] str, SqlConnection connection)
        { 
            DataSet data_set = new DataSet();

            try
            {
                String pk_column = "";
                List<String> pk_list = new List<String>();
                // table column info
                DataTable def_datatable = SelectCommand('D', str, null, connection);
                for (int i = 0; i < def_datatable.Rows.Count; i++)
                {
                    for (int j = 0; j < def_datatable.Columns.Count; j++)
                    {
                        if(def_datatable.Rows[i][j].ToString() == "PK")
                        {
                            pk_list.Add(def_datatable.Rows[i][0].ToString());
                        }
                    }
                }

                for(int i = 0; i< pk_list.Count; i++)
                {
                    if(i == 0)
                    {
                        pk_column = pk_list[0];
                    } else
                    {
                        pk_column = pk_column + ", " + pk_list[i];
                    }
                }

                DataTable row_datatable = SelectCommand('R', str, pk_column, connection);

                data_set.Tables.Add(def_datatable);
                data_set.Tables.Add(row_datatable);
            }
            catch (Exception e)
            {
                log.Error(e.ToString());
            }

            return data_set;
        }

        // select command
        public DataTable SelectCommand(char type, String[] str, String pk_column, SqlConnection connection)
        {
            String command = "";
            String table_name = "";
            DataTable data_table = null;
            
            using(SqlCommand cmd = connection.CreateCommand())
            {
                String schema = str[1];
                String table = str[2];
                // table column info
                if (type == 'D')
                {
                    table_name = "defResult";
                    command = String.Format("SELECT A.COLUMN_NAME, A.DATA_TYPE, D.MAX_LENGTH, F.TYPE " +
                    "FROM INFORMATION_SCHEMA.COLUMNS A " +
                    "INNER JOIN SYS.SCHEMAS B ON A.TABLE_SCHEMA = B.NAME " +
                    "INNER JOIN SYS.TABLES C ON B.SCHEMA_ID = C.SCHEMA_ID " +
                    "INNER JOIN SYS.COLUMNS D ON C.OBJECT_ID = D.OBJECT_ID AND A.COLUMN_NAME = D.NAME " +
                    "LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE E ON A.TABLE_CATALOG = E.TABLE_CATALOG AND E.COLUMN_NAME = A.COLUMN_NAME " +
                    "LEFT JOIN SYS.KEY_CONSTRAINTS F ON E.CONSTRAINT_NAME = F.NAME " +
                    "WHERE A.TABLE_SCHEMA = '{0}' AND A.TABLE_NAME = '{1}'", schema, table);
                }
                // table row data
                else if (type == 'R')
                {
                    String start_row = str[3];
                    String end_row = str[4];
                    table_name = "rowResult";
                    command = String.Format("SELECT NO, ID FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY({0})) ROWNUM FROM {1}.{2}) R WHERE R.ROWNUM BETWEEN {3} AND {4}", pk_column, schema, table, start_row, end_row);
                }

                cmd.CommandText = command;

                SqlDataAdapter sda = new SqlDataAdapter(cmd);
                data_table = new DataTable(table_name);
                sda.Fill(data_table);
            }

            return data_table;
        }

        // process insert query
        public int InsertData(MsgPackStruct.MsgStruct.RowListStc row_list_stc, SqlConnection connection, ProcessDatabase.Struct.DefStc def_struct)
        {
            int result = 0;
            String col_list = "";
            String data_list = "";
            try
            {
                using(SqlCommand cmd = connection.CreateCommand())
                {
                    for (int i = 0; i < row_list_stc.row_list.Count; i++)
                    {
                        col_list = row_list_stc.row_list[i].data_list[0].col_name;
                        data_list = "\'" + row_list_stc.row_list[i].data_list[0].col_data.Trim() + "\'";
                        for (int j = 1; j < row_list_stc.row_list[i].data_list.Count; j++)
                        {
                            col_list = col_list + ", " + row_list_stc.row_list[i].data_list[j].col_name;
                            data_list = data_list + ", \'" + row_list_stc.row_list[i].data_list[j].col_data.Trim() + "\'";
                        }

                        cmd.CommandText = String.Format("insert into {0}.{1} ({2}) values({3})", def_struct.schema, def_struct.table, col_list, data_list);
                        try
                        {
                            result += cmd.ExecuteNonQuery();
                        } catch(SqlException e)
                        {
                            if(e.Number == 2627)
                            {
                                Console.WriteLine((i + 1) + "th row insert fail. " + col_list + " (" + data_list + ") duplicate value");
                            } else
                            {
                                Console.WriteLine((i + 1) + "th row insert fail.");
                            }
                            log.Error(e.ToString());
                        }
                        
                    }
                }
            }
            catch (Exception e)
            {
                log.Error(e.ToString());
                return 0;
            }

            return result;
        }
    }
}
