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

using System.IO;                // FileInfo
using NLog;
using System.Threading;         // Sleep
using System.Data;              // DataSet
using System.Data.SqlClient;    // SqlConnection

namespace CSProject
{
    class Program
    {
        static Logger log = LogManager.GetLogger("agent_logger");
        static String home_path = "C:\\Users\\hwajin\\source\\repos\\CSProject\\CSProject\\";

        static void Main(String[] args)
        {
            log.Debug("CSProject start");

            ProcessCommand();   // processing user command
        }

        // processing user command
        public static void ProcessCommand()
        {
            log.Debug("ProcessCommand start");
            String path = "";   // file path
            ProcessDatabase.Process process_database = new ProcessDatabase.Process();
            ProcessFile.Files process_file = new ProcessFile.Files();

            ProcessDatabase.Struct.DefStc def_struct = new ProcessDatabase.Struct.DefStc();
            List<ProcessDatabase.Struct.ColStc> col_list = new List<ProcessDatabase.Struct.ColStc>();

            MsgPackStruct.MsgStruct.DataListStc data_list_struct = new MsgPackStruct.MsgStruct.DataListStc();
            MsgPackStruct.MsgStruct.RowListStc row_list_struct = new MsgPackStruct.MsgStruct.RowListStc();

            while (true)
            {
                Console.Write("test> ");
                String read = Console.ReadLine();
                String[] action = read.Split(' ');

                // select database
                if (action[0] == "get")
                {
                    if (action.Length == 6)
                    {
                        String conn_file_path = action[5];
                        String select_result = "";

                        // database connect
                        ConnDB.Connect connDB = new ConnDB.Connect();
                        ConnDB.Struct.ConnStateStc connect_result = connDB.ConnectDB(conn_file_path, home_path);

                        bool isConnect = connect_result.is_connect;    // return database connection status
                        if (!isConnect)
                        {
                            log.Error("Database Connect Fail");
                            return;
                        }

                        Console.WriteLine("### db connect ###");
                        log.Debug("Database Connect Success");

                        log.Debug("[get] command start");
                        def_struct.schema = action[1];
                        def_struct.table = action[2];

                        row_list_struct.schema = action[1];
                        row_list_struct.table = action[2];

                        if (conn_file_path == "csconn")
                        {
                            // data processing. return select result
                            DataSet process_result = process_database.ProcessSelect(action, connect_result.connection);

                            if (process_result.Tables.Count > 0)
                            {
                                def_struct = ProcessDefInfo(process_result.Tables[0], def_struct);

                                row_list_struct.row_list = new List<MsgPackStruct.MsgStruct.DataListStc>();
                                foreach (DataRow row in process_result.Tables[1].Rows)
                                {
                                    data_list_struct.data_list = new List<MsgPackStruct.MsgStruct.DataStc>();
                                    foreach (DataColumn col in process_result.Tables[1].Columns)
                                    {
                                        MsgPackStruct.MsgStruct.DataStc data_struct = new MsgPackStruct.MsgStruct.DataStc();
                                        data_struct.col_name = col.ColumnName;
                                        data_struct.col_data = row[col].ToString();
                                        data_list_struct.data_list.Add(data_struct);
                                    }
                                    row_list_struct.row_list.Add(data_list_struct);
                                }
                            }

                            select_result = ReturnResult(def_struct, row_list_struct);   // return result data
                            byte[] trc_buffer = MessagePack.MessagePackSerializer.Serialize(row_list_struct);
                            path = String.Format(home_path + "dat\\{0}_{1}_", def_struct.schema, def_struct.table) + System.DateTime.Now.ToString("yyyyMMddHHmmss");

                            process_file.CreateFile(path + ".dat", select_result);
                            process_file.CreateFile(path + ".trc", trc_buffer);
                        } else if(conn_file_path == "recvconn")
                        {
                            String[] str = { "", row_list_struct.schema, row_list_struct.table };
                            DataTable def_datatable = process_database.SelectCommand('D', str, "", connect_result.connection);

                            // def info
                            def_struct = new ProcessDatabase.Struct.DefStc();
                            def_struct.schema = row_list_struct.schema;
                            def_struct.table = row_list_struct.table;
                            def_struct = ProcessDefInfo(def_datatable, def_struct);

                            select_result = ReturnResult(def_struct, new MsgPackStruct.MsgStruct.RowListStc());
                        } else
                        {
                            log.Error("[get] no enough arguments");
                            Console.WriteLine("### no enough arguments  ###");
                        }

                        Console.WriteLine();
                        Console.WriteLine(select_result);
                    }
                    else
                    {
                        log.Error("[get] no enough arguments");
                        Console.WriteLine("### no enough arguments  ###");
                    }
                }
                // create recv process
                else if (action[0] == "recv")
                {
                    if (action.Length == 3)
                    {
                        log.Debug("[recv] command start");
                        String ip = action[1];
                        String port = action[2];

                        bool isCreate = CreateRecvProcess.Create.CreateProcess(ip, port, home_path);
                        if(!isCreate)
                        {
                            log.Error("create recv process fail");
                            return;
                        }

                        log.Debug("create recv process success");
                        Console.WriteLine("create process success");
                    }
                    else
                    {
                        log.Error("[recv] no enough arguments");
                        Console.WriteLine("### no enough arguments  ###");
                    }
                }
                // send data to recv process
                else if (action[0] == "send")
                {
                    if (action.Length == 4)
                    {
                        log.Debug("[send] command start");
                        String ip = action[1];
                        String port = action[2];    // recv port
                        String trc_file = action[3];    // trc file name
                        SendData.Send send_data = new SendData.Send();

                        send_data.SendTrc(ip, port, trc_file, home_path);
                    }
                    else
                    {
                        log.Error("[send] no enough arguments");
                        Console.WriteLine("### no enough arguments  ###");
                    }
                }
                // insert recv database
                else if (action[0] == "post")
                {
                    if(action.Length == 3)
                    {
                        log.Debug("[post] command start");
                        // 1. recv trc file open
                        String recv_path = home_path + "dat\\" + action[1] + ".trc";
                        FileInfo fileInfo = new FileInfo(recv_path);

                        // 1-1. check file exist
                        if (!fileInfo.Exists)
                        {
                            Console.WriteLine("non-existent trc file path");
                            log.Error("non-existent recv trace file path");
                        } else
                        {
                            // 2. db connect -> recvconn.conn
                            ConnDB.Connect connDB = new ConnDB.Connect();
                            // action[2] : db conn file name
                            ConnDB.Struct.ConnStateStc connect_result = connDB.ConnectDB(action[2], home_path);

                            byte[] data_buffer = process_file.OpenTrcFile(recv_path);   // return data
                            row_list_struct = MessagePack.MessagePackSerializer.Deserialize<MsgPackStruct.MsgStruct.RowListStc>(data_buffer);

                            String[] str = { "", row_list_struct.schema, row_list_struct.table};
                            DataTable def_datatable = process_database.SelectCommand('D', str, "", connect_result.connection);

                            // def info
                            def_struct = new ProcessDatabase.Struct.DefStc();
                            def_struct.schema = row_list_struct.schema;
                            def_struct.table = row_list_struct.table;
                            def_struct = ProcessDefInfo(def_datatable, def_struct);

                            // insert into recv database
                            int rowcnt = process_database.InsertData(row_list_struct, connect_result.connection, def_struct);
                            if(rowcnt != 0)
                            {
                                Console.WriteLine(rowcnt + " rows insert success\n");
                            }

                            String select_result = ReturnResult(def_struct, new MsgPackStruct.MsgStruct.RowListStc());   // return result data
                            
                            Console.WriteLine();
                            Console.WriteLine(select_result);
                        }
                    }
                    else
                    {
                        log.Error("[post] no enough arguments");
                        Console.WriteLine("### no enough arguments  ###");
                    }
                }
                // process exit
                else if (action[0] == "exit")
                {
                    log.Debug("process exit");
                    Console.WriteLine("### process exit ###");
                    return;
                }
                else
                {
                    log.Error("incorrect instruction");
                    Console.WriteLine("### incorrect instruction ###");
                }

                Thread.Sleep(1000);
            }
        }

        // processing result data
        public static String ReturnResult(ProcessDatabase.Struct.DefStc def_struct, MsgPackStruct.MsgStruct.RowListStc row_list_struct)
        {
            String result = String.Format("# def info\nschema : {0}, table : {1}\n", def_struct.schema, def_struct.table);

            // processing def data
            for (int i = 0; i < def_struct.col_list.Count; i++)
            {
                result += String.Format("col{0} : {1},\ttype : {2},\tlength : {3},\tpk : {4}\n", i + 1, def_struct.col_list[i].colmn_name, def_struct.col_list[i].colmn_type, def_struct.col_list[i].colmn_length, def_struct.col_list[i].colmn_pk_yn);
            }

            if(row_list_struct.row_list != null)
            {
                result += "\n# row data\n";
                int k = 1;
                // processing row data
                Console.WriteLine("row cnt : "+ row_list_struct.row_list.Count);
                for (int i = 0; i < row_list_struct.row_list.Count; i++)
                {
                    result += String.Format("row{0}", k++);
                    for (int j = 0; j < row_list_struct.row_list[i].data_list.Count; j++)
                    {
                        result += String.Format("  {0} : {1}  ", row_list_struct.row_list[i].data_list[j].col_name, row_list_struct.row_list[i].data_list[j].col_data);
                    }
                    result += "\n";
                }

                result = result + "\n" + (k - 1) + " rows select success.\n";
            }

            return result;
        }

        // process def info. def info 정보를 구조체에 저장
        public static ProcessDatabase.Struct.DefStc ProcessDefInfo(DataTable def_datatable, ProcessDatabase.Struct.DefStc def_struct)
        {
            String pk_yn = "X";
            List<ProcessDatabase.Struct.ColStc> col_list = new List<ProcessDatabase.Struct.ColStc>();

            foreach (DataRow row in def_datatable.Rows)
            {
                ProcessDatabase.Struct.ColStc col_struct = new ProcessDatabase.Struct.ColStc();
                col_struct.colmn_name = row[0].ToString();
                col_struct.colmn_type = row[1].ToString();
                col_struct.colmn_length = int.Parse(row[2].ToString());

                if (row[3].ToString() == "PK")
                {
                    pk_yn = "O";
                }
                else
                {
                    pk_yn = "X";
                }
                col_struct.colmn_pk_yn = pk_yn;
                col_list.Add(col_struct);
            }
            def_struct.col_list = col_list;

            return def_struct;
        }
    }
}
