using DevExpress.Utils.Drawing;
using LFP_Manager.DataStructure;
using System;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace LFP_Manager.Utils
{
    class csDbUtils
    {
        // Query - SELECT * FROM TABLE_NAME like('%neon%',field_name) - 문자를 포함하는 데이터 검색
        public static string DbFileName = @"LFPS-48100S-J_INV.db";
        public static string DbFilePathFormat = @"\db\";
        public static string DbFileNameFormat = @"\db\{0}_{1}.db";
        public static string DbSchemaTableFileName = @"\sql\DB_SCHEMA_TABLE.sql";
        public static string ModuleTableName = @"TModule";

        public static string LogDbFilePath = @"db";
        public static string LogDbFileNameFormat = @"{0}\{1}_{2}_LOG.DB";
        public static string LogDbSchemaTableFileName = @"sql\LOG_DB_SCHEMA_TABLE.sql";

        public static DateTime Delay(int ms)
        {
            DateTime thisMoment = DateTime.Now;
            TimeSpan duration = new TimeSpan(0, 0, 0, 0, ms);
            DateTime afterMoment = thisMoment.Add(duration);

            while (afterMoment >= thisMoment)
            {
                Application.DoEvents();
                thisMoment = DateTime.Now;
            }

            return DateTime.Now;
        }

        //트랜잭션 시작
        public static void BeginTran(SQLiteConnection conn)
        {
            SQLiteCommand command = new SQLiteCommand("Begin", conn);
            command.ExecuteNonQuery();
            command.Dispose();
        }

        //트랜잭션 완료
        public static void CommitTran(SQLiteConnection conn)
        {
            SQLiteCommand command = new SQLiteCommand("Commit", conn);
            command.ExecuteNonQuery();
            command.Dispose();
        }

        #region COMMON DB HELPERS
        private static DataTable ExecSelect(string dbPath, string sql)
        {
            var dt = new DataTable();
            using (var conn = new SQLiteConnection(@"Data Source=" + dbPath))
            using (var adp = new SQLiteDataAdapter(sql, conn))
            {
                conn.Open();
                adp.AcceptChangesDuringFill = false;
                adp.Fill(dt);
            }
            return dt;
        }
        #endregion 

        #region CREATE DATABASE

        public static void DbCreate(string mSN, string bName)
        {
            string result = "";
            string dbFilename = String.Format(DbFileNameFormat, MakeLotNumber(mSN, bName), bName);
            string dbFilePath = Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilePath)) == false)
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(dbFilePath));

            if (File.Exists(dbFilePath) == true)
            {
                //throw new Exception("Already have db file - Failed to create db file");
                return;
            }
            else
            {
                // Create database
                SQLiteConnection.CreateFile(dbFilePath);
            }

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Create table
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        string schemaFile = Path.GetDirectoryName(Application.ExecutablePath) + DbSchemaTableFileName;
                        string query = File.ReadAllText(schemaFile);

                        command.CommandText = query;
                        SQLiteDataReader reader = null;
                        reader = command.ExecuteReader();
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return;
        }

        public static void LogDbCreate(string aModel)
        {
            string result = "";
            string ModelName = aModel;
            //public static string LogDbFileNameFormat = @"\{0}\{1}_{2}_LOG.DB";
            string dbFilename = String.Format(LogDbFileNameFormat
                , String.Format("{0:yyMM}", DateTime.Now)
                , String.Format("{0:yyMMdd}", DateTime.Now)
                , ModelName
                );
            string dbFilePath = Path.GetDirectoryName(Application.ExecutablePath) + LogDbFilePath + dbFilename;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilePath)) == false)
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(dbFilePath));

            if (File.Exists(dbFilePath) == true)
            {
                //throw new Exception("Already have db file - Failed to create db file");
                return;
            }
            else
            {
                // Create database
                SQLiteConnection.CreateFile(dbFilePath);
            }

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Create table
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        string schemaFile = Path.GetDirectoryName(Application.ExecutablePath) + LogDbSchemaTableFileName;
                        string query = File.ReadAllText(schemaFile);

                        command.CommandText = query;
                        SQLiteDataReader reader = null;
                        reader = command.ExecuteReader();
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return;
        }

        #endregion

        #region DB CREATE FUNCTION (LOG)
        public static void DbCreateLOG(CommConfig aConfig)
        {
            string modelName = csConstData.UART_MODEL[aConfig.UartModelIndex];

            string folder = string.Format("{0:yyMM}", DateTime.Now);
            string file = string.Format("{0}_{1}_LOG.DB", string.Format("{0:yyMMdd}", DateTime.Now), modelName);
            string exeDir = Path.GetDirectoryName(Application.ExecutablePath);
            string dbFilePath = csUtils.CombineApp(exeDir, LogDbFilePath, folder, file);

            string dir = Path.GetDirectoryName(dbFilePath);
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

            if (File.Exists(dbFilePath)) return;

            SQLiteConnection.CreateFile(dbFilePath);

            string schemaFile = csUtils.CombineApp(LogDbSchemaTableFileName);
            string query = File.ReadAllText(schemaFile);

            using (var connection = new SQLiteConnection(@"Data Source=" + dbFilePath))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = query;
                    using (var reader = command.ExecuteReader()) { /* schema create */ }
                }
            }
        }
        #endregion


        #region MAKE LOT NUMBER
        public static string MakeLotNumber(string mSN)
        {
            string LotNo = mSN.Substring(1, 11) + "000";
            return LotNo;
        }

        public static string MakeLotNumber(string mSN, string bName)
        {
            string LotNo;

            if (bName == "BMCB_J")
                LotNo = mSN.Substring(0, 10) + "0000";
            else
                LotNo = mSN.Substring(1, 11) + "000";
            return LotNo;
        }

        #endregion

        #region Excute Database
        public static DataTable DbSqlExcute(string path, string query, bool create_db_flag = false)
        {
            string result = "";
            DataTable dtResult = null;
            string dbFilename = Path.GetDirectoryName(path) + DbFileName;

            if (Directory.Exists(Path.GetDirectoryName(dbFilename)) == false)
                Directory.CreateDirectory(Path.GetDirectoryName(dbFilename));

            // Create database
            if (File.Exists(dbFilename) == false)
            {
                if (create_db_flag == true)
                    SQLiteConnection.CreateFile(dbFilename);
                else
                    throw new Exception("No database file - " + DbFileName);
            }

            // Open database
            string strConn = @"Data Source=" + dbFilename;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Create table
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //command.CommandText = "CREATE TABLE File (Name text, Size bigint, Modified datetime);";
                        command.CommandText = query;
                        SQLiteDataReader reader = null;
                        //command.ExecuteNonQuery();
                        reader = command.ExecuteReader();
                        dtResult = new DataTable();
                        dtResult.Load(reader);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable DbSqlExcuteA1(string path, string query)
        {
            DataTable dtResult = null;
            string dbFilename = System.IO.Path.GetDirectoryName(path) + DbFileName;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilename)) == false)
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(dbFilename));

            if (File.Exists(dbFilename) == false)
                // Create database
                SQLiteConnection.CreateFile(dbFilename);

            // Open database
            string strConn = @"Data Source=" + dbFilename;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Excute Query
                    var adpt = new SQLiteDataAdapter(query, connection);
                    DataSet ds = new DataSet();
                    adpt.Fill(ds);

                    dtResult = ds.Tables[0];
                }
                catch (Exception e)
                {
                    throw e;
                }
                finally
                {
                    connection.Close();
                }
            }
            return dtResult;
        }

        public static DataTable DbSqlExcuteA(string lot, string query, string bName, bool create_db_flag = false)
        {
            string result = "";
            DataTable dtResult = new DataTable();
            string lotNo = lot;
            string dbFilename = String.Format(DbFileNameFormat, lotNo, bName);
            string dbFilePath = Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(Path.GetDirectoryName(dbFilePath)) == false)
            {
                if (create_db_flag)
                    Directory.CreateDirectory(Path.GetDirectoryName(dbFilePath));
                else
                    throw new Exception("No DB file path - " + Path.GetDirectoryName(dbFilePath));
            }
            if (File.Exists(dbFilePath) == false)
            {
                if (create_db_flag)
                    SQLiteConnection.CreateFile(dbFilePath);
                else
                    throw new Exception(String.Format("No DB file - DbSqlExcuteA ({0})", dbFilename));
            }

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Excute Query
                    var adpt = new SQLiteDataAdapter(query, connection);
                    DataSet ds = new DataSet();
                    adpt.Fill(ds);
                    if (ds.Tables.Count > 0)
                        dtResult = ds.Tables[0];
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable DbSqlExcuteA2(string dbFileName, string query, bool create_db_flag = false)
        {
            string result = "";
            DataTable dtResult = new DataTable();
            string lotNo = dbFileName.Substring(0, 9);
            string dbFilename = String.Format(DbFilePathFormat, lotNo) + dbFileName;
            string dbFilePath = Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(Path.GetDirectoryName(dbFilePath)) == false)
            {
                if (create_db_flag)
                    Directory.CreateDirectory(Path.GetDirectoryName(dbFilePath));
                else
                    throw new Exception("No DB file path - " + Path.GetDirectoryName(dbFilePath));
            }
            if (File.Exists(dbFilePath) == false)
            {
                if (create_db_flag)
                    SQLiteConnection.CreateFile(dbFilePath);
                else
                    throw new Exception(String.Format("No DB file - DbSqlExcuteA ({0})", dbFilename));
            }

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Excute Query
                    var adpt = new SQLiteDataAdapter(query, connection);
                    DataSet ds = new DataSet();
                    adpt.Fill(ds);
                    if (ds.Tables.Count > 0)
                        dtResult = ds.Tables[0];
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }
        public static DataTable DbSqlExcuteA3(string dbFileName, string query, bool create_db_flag = false)
        {
            string result = "";
            DataTable dtResult = new DataTable();
            string dbFilePath = dbFileName;

            if (Directory.Exists(Path.GetDirectoryName(dbFilePath)) == false)
            {
                if (create_db_flag)
                    Directory.CreateDirectory(Path.GetDirectoryName(dbFilePath));
                else
                    throw new Exception("No DB file path - " + Path.GetDirectoryName(dbFilePath));
            }
            if (File.Exists(dbFilePath) == false)
            {
                if (create_db_flag)
                    SQLiteConnection.CreateFile(dbFilePath);
                else
                    throw new Exception(String.Format("No DB file - DbSqlExcuteA ({0})", dbFileName));
            }

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                connection.Open();
                try
                {
                    // Excute Query
                    var adpt = new SQLiteDataAdapter(query, connection);
                    DataSet ds = new DataSet();
                    adpt.Fill(ds);
                    if (ds.Tables.Count > 0)
                        dtResult = ds.Tables[0];
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }

        #endregion

        #region BMS DATA INSERT AND UPDATE

        public static string GetDbFilePath(string modelName, DateTime dateTime)
        {
            string folder = string.Format("{0:yyMM}", dateTime);               // 2512
            string file = string.Format("{0}_{1}_LOG.DB", string.Format("{0:yyMMdd}", dateTime), modelName);   // 251221_LFPM-48100SB_LOG.DB
            string exeDir = Path.GetDirectoryName(Application.ExecutablePath);
            string dbPath = csUtils.CombineApp(exeDir, LogDbFilePath, folder, file);    // db\2512\251221_LFPM-48100SB_LOG.DB
            return dbPath;
        }

        public static void BmsLogDataInsert(string ModelName, ref CsDeviceData.DeviceModuleData mData, DateTime dateTime, int cvUnit, int log_type = 0)
        {
            string result = "";
            string dbFilePath = GetDbFilePath(ModelName, dateTime);

            if (Directory.Exists(Path.GetDirectoryName(dbFilePath)) == false)
                throw new Exception("No Log DB file path");
            if (File.Exists(dbFilePath) == false)
                throw new Exception(String.Format("No Log DB file - BmsLogDataInsert ({0})", dbFilePath));

            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                SQLiteTransaction tran = null;

                try
                {
                    connection.Open();

                    // 기존 BeginTran/CommitTran을 유지하고 싶으면 tran 대신 그걸 쓰셔도 됩니다.
                    // 다만 C# 7.3에서는 아래 표준 방식이 제일 안전합니다.
                    tran = connection.BeginTransaction();

                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.Transaction = tran;

                        // 스키마 기준: cell 15, temp 4 고정
                        const int CELL_MAX = 15;
                        const int TEMP_MAX = 4;

                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = "INSERT INTO TModuleValue ("
                            + "create_date"
                            + ", log_type"
                            + ", module_no"
                            + ", model_name"
                            + ", module_sn"
                            + ", fw_ver"
                            + ", comm_fail"
                            + ", op_status"
                            + ", alarm_status"
                            + ", warning"
                            + ", fault"
                            + ", module_voltage"
                            + ", module_current"
                            + ", module_soc"
                            + ", module_soh"
                            + ", module_cyclecount"
                            + ", cv_diff";

                        for (int i = 0; i < CELL_MAX; i++)
                            command.CommandText += $", cell_voltage_{i + 1:00}";

                        command.CommandText += ", tp_diff";

                        for (int i = 0; i < TEMP_MAX; i++)
                            command.CommandText += $", temperature_{i + 1:00}";

                        command.CommandText += ""
                            + ", ext1_temp"
                            + ", ext2_temp"
                            + ", chg_option"
                            + ") VALUES (";

                        int total = 16 + 1 + CELL_MAX + 1 + TEMP_MAX + 3;
                        // 16(기본 0~15) + cv_diff(1) + cell15 + tp_diff(1) + temp4 + ext1/ext2/chg(3)

                        for (int i = 0; i < total - 1; i++) command.CommandText += "?,";
                        command.CommandText += "?);";

                        for (int i = 0; i < total; i++)
                            command.Parameters.Add(new SQLiteParameter());

                        int j = 0;

                        command.Parameters[j++].Value = dateTime;                              // create_date
                        command.Parameters[j++].Value = log_type;                              // log_type
                        command.Parameters[j++].Value = mData.mNo;                             // module_no
                        command.Parameters[j++].Value = (object)mData.Information.ModelName ?? DBNull.Value;
                        command.Parameters[j++].Value = (object)mData.Information.HwSerialNumber ?? DBNull.Value;
                        command.Parameters[j++].Value = (object)mData.Information.SwProductRev ?? DBNull.Value;

                        command.Parameters[j++].Value = mData.ShelfCommFail;                  // comm_fail (bool -> 0/1도 가능)
                        command.Parameters[j++].Value = mData.StatusData.status;              // op_status
                        command.Parameters[j++].Value = mData.StatusData.batteryStatus;       // alarm_status
                        command.Parameters[j++].Value = mData.StatusData.warning;             // warning
                        command.Parameters[j++].Value = mData.StatusData.protect;             // fault

                        command.Parameters[j++].Value = mData.ValueData.voltage / 10.0;       // module_voltage
                        command.Parameters[j++].Value = mData.ValueData.current / 10.0;       // module_current
                        command.Parameters[j++].Value = mData.ValueData.SOC / 10.0;           // module_soc
                        command.Parameters[j++].Value = mData.ValueData.SOH / 10.0;           // module_soh
                        command.Parameters[j++].Value = mData.ValueData.cycleCount;           // module_cyclecount

                        // cv_diff = 셀 전압 max-min (V)
                        object cvDiffValue = DBNull.Value;
                        if (mData.ValueData.CellVoltage != null && mData.cellQty > 0)
                        {
                            int n = Math.Min(mData.cellQty, CELL_MAX);
                            double minV = double.MaxValue;
                            double maxV = double.MinValue;

                            for (int i = 0; i < n; i++)
                            {
                                double v = mData.ValueData.CellVoltage[i] / (double)cvUnit;
                                if (v < minV) minV = v;
                                if (v > maxV) maxV = v;
                            }

                            if (minV != double.MaxValue)
                                cvDiffValue = (maxV - minV);
                        }
                        command.Parameters[j++].Value = cvDiffValue;                          // cv_diff

                        // cell_voltage_01..15 (없으면 NULL)
                        for (int i = 0; i < CELL_MAX; i++)
                        {
                            if (mData.ValueData.CellVoltage != null && i < mData.cellQty)
                                command.Parameters[j++].Value = mData.ValueData.CellVoltage[i] / (double)cvUnit;
                            else
                                command.Parameters[j++].Value = DBNull.Value;
                        }

                        // tp_diff = 온도 max-min (°C)
                        object tpDiffValue = DBNull.Value;
                        if (mData.ValueData.CellTemperature != null && mData.tempQty > 0)
                        {
                            int n = Math.Min(mData.tempQty, TEMP_MAX);
                            double minT = double.MaxValue;
                            double maxT = double.MinValue;

                            for (int i = 0; i < n; i++)
                            {
                                double t = mData.ValueData.CellTemperature[i] / 10.0;
                                if (t < minT) minT = t;
                                if (t > maxT) maxT = t;
                            }

                            if (minT != double.MaxValue)
                                tpDiffValue = (maxT - minT);
                        }
                        command.Parameters[j++].Value = tpDiffValue;                          // tp_diff

                        // temperature_01..04 (없으면 NULL)
                        for (int i = 0; i < TEMP_MAX; i++)
                        {
                            if (mData.ValueData.CellTemperature != null && i < mData.tempQty)
                                command.Parameters[j++].Value = mData.ValueData.CellTemperature[i] / 10.0;
                            else
                                command.Parameters[j++].Value = DBNull.Value;
                        }

                        command.Parameters[j++].Value = mData.ValueData.MosTemperature / 10.0; // ext1_temp
                        command.Parameters[j++].Value = mData.ValueData.AmbTemperature / 10.0; // ext2_temp
                        command.Parameters[j++].Value = (short)mData.CalibrationData.Current.ChargeOption; // chg_option

                        if (j != total)
                            throw new Exception(string.Format("Parameter mismatch: j={0}, total={1}", j, total));

                        command.ExecuteNonQuery();
                    }
                    tran.Commit();
                }
                catch (Exception e)
                {
                    try
                    {
                        if (tran != null) tran.Rollback();
                    }
                    catch { /* rollback 실패는 무시 or 로그 */ }

                    result = e.Message;
                }
                finally
                {
                    if (tran != null) tran.Dispose();

                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
        }

        public static DataTable GetDataTable(string bSN, string aTableName, string bName)
        {
            string result = "";
            DataTable dtResult = null;
            string dbFilename = String.Format(DbFileNameFormat, MakeLotNumber(bSN, bName), bName);
            string dbFilePath = Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(Path.GetDirectoryName(dbFilePath)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFilePath) == false)
                throw new Exception("No DB file");
            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait();
                WaitForm.StartPosition = FormStartPosition.CenterScreen;
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = String.Format("SELECT * FROM {0}", aTableName);
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        dtResult = new DataTable();
                        adapter.Fill(dtResult);
                        dtResult.TableName = aTableName;

                        CommitTran(connection);

                        System.Windows.Forms.Application.DoEvents();
                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable GetDataTableBySelect(string bSN, string sql, string aTableName, string bName)
        {
            string result = "";
            DataTable dtResult = null;
            string dbFilename = String.Format(DbFileNameFormat, MakeLotNumber(bSN, bName), bName);
            string dbFilePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilePath)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFilePath) == false)
                throw new Exception(String.Format("No DB file - {0}", dbFilePath));
            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait();
                WaitForm.StartPosition = FormStartPosition.CenterScreen;
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = sql;
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        dtResult = new DataTable();
                        adapter.Fill(dtResult);
                        dtResult.TableName = aTableName;

                        System.Windows.Forms.Application.DoEvents();
                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));

                        CommitTran(connection);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "")
                        throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable GetDataTableBySelectFromDbName(string dbFileName, string sql, string aTableName, string bName)
        {
            string result = "";
            DataTable dtResult = null;

            string lotNo = csDbUtils.MakeLotNumber(dbFileName.Substring(0, 8), bName);
            string dbFilename = String.Format(@"\db\{0}\{1}", lotNo, dbFileName);
            string dbFilePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + dbFilename;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilePath)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFilePath) == false)
                throw new Exception(String.Format("No DB file - {0}", dbFilePath));
            // Open database
            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait();
                WaitForm.StartPosition = FormStartPosition.CenterScreen;
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = sql;
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        dtResult = new DataTable();
                        adapter.Fill(dtResult);
                        dtResult.TableName = aTableName;

                        System.Windows.Forms.Application.DoEvents();
                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));

                        CommitTran(connection);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "")
                        throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable GetDataTableByFileName(string fileName, string sql, string aTableName)
        {
            string result = "";
            DataTable dtResult = null;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(fileName)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(fileName) == false)
                throw new Exception("No DB file");
            // Open database
            string strConn = @"Data Source=" + fileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait();
                WaitForm.StartPosition = FormStartPosition.CenterScreen;
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = sql;
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        dtResult = new DataTable();
                        adapter.Fill(dtResult);
                        dtResult.TableName = aTableName;

                        System.Windows.Forms.Application.DoEvents();
                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));

                        CommitTran(connection);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "")
                        throw new Exception(result);
                }
            }
            return dtResult;
        }

        public static DataTable GetModuleMatchTableByFgNo(string fg_no, string bName)
        {
            DataTable dtResult = null;

            int a, b, c;
            a = Convert.ToInt32(fg_no);
            b = a / 300 + 1;
            c = a % 300;
            if ((c == 0) && (b > 0)) b--;
            string mSN = String.Format("1{0:yy}{1:00}{2:000}", DateTime.Now, b, c);
            string lotNo = csDbUtils.MakeLotNumber(mSN, bName);
            string dbfilePath = Path.GetDirectoryName(Application.ExecutablePath) + String.Format(DbFilePathFormat, lotNo);
            string[] filePaths = Directory.GetFiles(dbfilePath, "*.db");

            if (filePaths.Length > 0)
            {
                for (int i = 0; i < filePaths.Length; i++)
                {
                    string fileName = Path.GetFileName(filePaths[i]);
                    byte[] bfilName = Encoding.ASCII.GetBytes(fileName);

                    dtResult = GetDataTableByFileName(filePaths[i], String.Format("SELECT * FROM TModuleMatch where fg_no={0}", fg_no), "TModuleMatch");
                    if ((dtResult != null) && (dtResult.Rows.Count == 1)) break;
                }
            }
            if ((dtResult == null) || (dtResult.Rows.Count == 0))
                throw new Exception(String.Format("No match data - {0}", fg_no));

            return dtResult;
        }

        public static void BmsPcbMatchDataInsert(string dbFileName, DataRow sRow)
        {
            string result = "";
            int resultCode = (int)SQLiteErrorCode.Unknown;

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFileName)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFileName) == false)
                throw new Exception(String.Format("No DB file - BmsDataInsert ({0})", dbFileName));

            // Open database
            string strConn = @"Data Source=" + dbFileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait
                {
                    StartPosition = FormStartPosition.CenterScreen
                };

                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = "INSERT INTO"
                            + " TBmsPcbMatchTable"
                            + " (PCB1_SERIAL_NUMBER, MODULE_SERIAL_NUMBER, create_date, modify_date)"                                                          // 2
                            + " Values (?,?,?,?);";
                        SQLiteParameter[] p = new SQLiteParameter[4];

                        for (int i = 0; i < 4; i++)
                        {
                            p[i] = new SQLiteParameter();
                            command.Parameters.Add(p[i]);
                        }

                        int j = 0;
                        p[j++].Value = sRow["PCB1_SERIAL_NUMBER"];                          // pcb_sn
                        p[j++].Value = sRow["MODULE_SERIAL_NUMBER"];                        // module_sn
                        p[j++].Value = DateTime.Now;                                        // create_date
                        p[j++].Value = DateTime.Now;                                        // modify_date

                        command.ExecuteNonQuery();

                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));
                        System.Windows.Forms.Application.DoEvents();

                        CommitTran(connection);
                    }
                }
                catch (SQLiteException sqle)
                {
                    // Handle DB exception
                    result = sqle.Message;
                    resultCode = sqle.ErrorCode;
                }
                catch (IndexOutOfRangeException ie)
                {
                    // If you think there might be a problem with index range in the loop, for example
                    result = ie.Message;
                    resultCode = 9999;
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                    resultCode = 9998;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "")
                    {
                        if (resultCode == 9998) throw new Exception(result);
                        else if (resultCode == 9999) throw new IndexOutOfRangeException(result);
                        else throw new SQLiteException((SQLiteErrorCode)resultCode, result);
                    }
                }
            }
        }

        public static void ErrorLogInsert(string dbFileName, DataRow sRow, string Process, string ErrType, string ErrMsg)
        {
            string result = "";

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFileName)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFileName) == false)
                throw new Exception(String.Format("No DB file - ErrorLogInsert ({0})", dbFileName));

            // Open database
            string strConn = @"Data Source=" + dbFileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = "INSERT INTO"
                            + " TErrorLogTable"
                            + " (create_date, MODULE_SERIAL_NUMBER, PCB1_SERIAL_NUMBER, PROCESS, ERROR_TYPE, ERROR_MSG)"                                                          // 2
                            + " Values (?,?,?,?,?,?);";
                        SQLiteParameter[] p = new SQLiteParameter[6];

                        for (int i = 0; i < 6; i++)
                        {
                            p[i] = new SQLiteParameter();
                            command.Parameters.Add(p[i]);
                        }

                        int j = 0;
                        p[j++].Value = DateTime.Now;                                        // create_date
                        p[j++].Value = sRow["MODULE_SERIAL_NUMBER"];                        // module_sn
                        p[j++].Value = sRow["PCB1_SERIAL_NUMBER"];                          // pcb_sn
                        p[j++].Value = Process;                                             // process
                        p[j++].Value = ErrType;                                             // error type
                        p[j++].Value = ErrMsg;                                              // error message

                        command.ExecuteNonQuery();

                        CommitTran(connection);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
        }

        #endregion

        #region ALARM HISTROTY DB FUNCTION
        public static DataTable GetBmsAlarmDataByDataTable(CommConfig aConfig, DateTime aDateTime, string qry)
        {
            string modelName = csConstData.UART_MODEL[aConfig.UartModelIndex];  // LFPM-48100SB
            string folder = string.Format("{0:yyMM}", aDateTime);               // 2512
            string file = string.Format("{0}_{1}_LOG.DB", string.Format("{0:yyMMdd}", aDateTime), modelName);   // 251221_LFPM-48100SB_LOG.DB
            string exeDir = Path.GetDirectoryName(Application.ExecutablePath);
            string dbPath = csUtils.CombineApp(exeDir, LogDbFilePath, folder, file);    // db\2512\251221_LFPM-48100SB_LOG.DB

            if (!File.Exists(dbPath)) return null;

            string sql = string.IsNullOrWhiteSpace(qry)
                ? "SELECT * FROM TAlarmHistory ORDER BY create_date DESC"
                : "SELECT * FROM TAlarmHistory " + qry;

            var dt = ExecSelect(dbPath, sql);
            dt.TableName = "TAlarmHistory";
            return dt;
        }

        public static DataTable GetBmsAlarmDataByDataTable1(string modelName, DateTime aDateTime, string qry)
        {
            // 결과 테이블(항상 non-null 반환)
            var dtResult = new DataTable("TAlarmHistory");

            // 일자 기준 DB 파일명 (yyMM/yyMMdd)
            string dbFilename = string.Format(
                LogDbFileNameFormat,
                aDateTime.ToString("yyMM"),
                aDateTime.ToString("yyMMdd"),
                modelName);     // \2512\{1}_{2}_LOG.DB";

            // 실행파일 경로 + 로그 DB 상대경로 + 파일명 안전 결합
            string exeDir = Path.GetDirectoryName(Application.ExecutablePath);
            if (string.IsNullOrEmpty(exeDir))
                exeDir = AppDomain.CurrentDomain.BaseDirectory;

            string logPath = (LogDbFilePath ?? string.Empty)
                                .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
            string dbDir = csUtils.CombineSafe(exeDir, logPath);
            string dbFilePath = csUtils.CombineSafe(dbDir, dbFilename);

            if (!File.Exists(dbFilePath))
                return dtResult; // 빈 테이블 반환

            // 당일 범위(00:00:00 ~ 다음날 00:00:00)
            DateTime start = aDateTime.Date;
            DateTime end = start.AddDays(1);

            // 조회 컬럼 지정 (SELECT * 지양)
            string cols = string.Join(", ", new[]
            {
                "create_date","alarm_code","alarm_status","alarm_cname","alarm_sname",
                "module_no","ct_no","alarm_param_1","alarm_param_2","alarm_param_3"
            });

            // 기본 WHERE (날짜 범위)
            string sql =
                $"SELECT {cols} FROM TAlarmHistory " +
                $"WHERE create_date >= @start AND create_date < @end";

            // 주의) qry는 그대로 붙이면 인젝션 위험. 반드시 신뢰 가능한 내부 생성 문자열만 사용하세요.
            if (!string.IsNullOrWhiteSpace(qry))
            {
                sql += " AND (" + qry + ")";
            }

            sql += " ORDER BY create_date DESC";

            using (var connection = new SQLiteConnection(@"Data Source=" + dbFilePath))
            {
                connection.Open();
                // ★ 성능/동시성 PRAGMA 적용 (WAL + busy_timeout 등) — C# 7.3 호환
                csDbUtils.SqlitePragmaHelper.ApplyPragmas(connection, false); // forReader: false

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = sql;

                    // 날짜 파라미터 바인딩 (SQLite는 TEXT/REAL/INTEGER 저장 허용. DbType.DateTime 사용)
                    var pStart = new SQLiteParameter("@start", DbType.DateTime) { Value = start };
                    var pEnd = new SQLiteParameter("@end", DbType.DateTime) { Value = end };
                    command.Parameters.Add(pStart);
                    command.Parameters.Add(pEnd);

                    using (var adapter = new SQLiteDataAdapter(command))
                    {
                        adapter.AcceptChangesDuringFill = false;
                        adapter.Fill(dtResult);
                    }
                }

                if (connection.State == ConnectionState.Open)
                    connection.Close();
            }

            return dtResult;
        }
        #endregion

        #region SELECT QUARY

        public static int DbQuaryExcuteToDataTable(string dbFileName, string Quary, ref DataTable rDT)
        {
            int result = 0;
            string resultStr = String.Empty;

            string strConn = @"Data Source=" + dbFileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = Quary;
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        rDT = new DataTable();
                        adapter.Fill(rDT);
                        //rDT.TableName = "TModuleValue";

                        CommitTran(connection);

                        result = rDT.Rows.Count;

                        System.Windows.Forms.Application.DoEvents();
                    }
                }
                catch (Exception e)
                {
                    resultStr = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (resultStr != "") throw new Exception(resultStr);
                }
            }
            return result;
        }

        public static void CreateLogDbFile(string dbFileName)
        {
            string result = "";

            if (Directory.Exists(Path.GetDirectoryName(dbFileName)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFileName) == true)
                throw new Exception(String.Format("Already have DB file - DbQuaryExcuteToDbFile({0})", dbFileName));

            // Create database
            SQLiteConnection.CreateFile(dbFileName);

            // Open database
            string strConn = @"Data Source=" + dbFileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                Forms.fmxWait WaitForm = new Forms.fmxWait();
                WaitForm.StartPosition = FormStartPosition.CenterScreen;
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    WaitForm.Show();
                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        string schemaFile = Path.GetDirectoryName(Application.ExecutablePath) + LogDbSchemaTableFileName;
                        string query = File.ReadAllText(schemaFile);

                        command.CommandText = query;
                        SQLiteDataReader reader = null;
                        reader = command.ExecuteReader();

                        WaitForm.SetDescription(String.Format("{0}//{1}", 1, 1));
                        System.Windows.Forms.Application.DoEvents();

                        CommitTran(connection);
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (WaitForm != null)
                        WaitForm.Close();
                    if (result != "") throw new Exception(result);
                }
            }
        }

        public static void DbInsertDataToDbFile(string dbFileName, DataRow dtRow)
        {
            string result = "";

            if (Directory.Exists(Path.GetDirectoryName(dbFileName)) == false)
                throw new Exception("No DB file path");
            if (File.Exists(dbFileName) == false)
                throw new Exception(String.Format("No DB file - DbInsertDataToDbFile({0})", dbFileName));

            // Open database
            string strConn = @"Data Source=" + dbFileName;
            using (var connection = new SQLiteConnection(strConn))
            {
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = String.Format("INSERT INTO TModuleValue (");
                        command.CommandText += "create_date"                                // 0
                            + ", module_no"                                                 // 1
                            + ", pcb_sn"                                                    // 2
                            + ", module_sn"                                                 // 3
                            + ", fw_ver"                                                    // 4
                            + ", comm_fail"                                                 // 5
                            + ", op_status"                                                 // 6
                            + ", alarm_status"                                              // 7
                            + ", warning"                                                   // 8
                            + ", fault"                                                     // 9
                            + ", module_voltage"                                            // 10
                            + ", module_current"                                            // 11
                            + ", module_soc"                                                // 12
                            + ", module_soh"                                                // 13
                            ;
                        for (int i = 0; i < 15; i++)
                        {
                            command.CommandText += String.Format(", cell_voltage_{0:00}", i + 1);       // 14 ~ 28; 15 cells
                        }
                        for (int i = 0; i < 6; i++)
                        {
                            command.CommandText += String.Format(", temperature_{0:00}", i + 1);        // 29 ~ 34; 6 temps
                        }
                        command.CommandText += ""
                            + ", chg_option"                                                // 35
                            + ", chg_cal"                                                   // 36
                            + ", dch_cal"                                                   // 37
                            + ")"
                            + " Values (";

                        int total = dtRow.Table.Columns.Count;

                        for (int i = 0; i < (total - 1); i++) command.CommandText += "?,";
                        command.CommandText += "?);";

                        SQLiteParameter[] p = new SQLiteParameter[total];

                        for (int i = 0; i < total; i++)
                        {
                            if (i == 0)
                                p[i] = new SQLiteParameter(DbType.DateTime);
                            else
                                p[i] = new SQLiteParameter();
                            command.Parameters.Add(p[i]);
                        }

                        int j = 0;
                        p[j++].Value = String.Format("{0:yyyy/MM/dd HH:mm:ss}", dtRow["create_date"]);          //  0 create_date
                        p[j++].Value = dtRow["module_no"];                                                      //  1 module_no
                        p[j++].Value = dtRow["pcb_sn"];                                                         //  2 pcb_sn
                        p[j++].Value = dtRow["module_sn"];                                                      //  3 module_sn
                        p[j++].Value = dtRow["fw_ver"];                                                         //  4 fw_ver
                        p[j++].Value = dtRow["comm_fail"];                                                      //  5 comm_fail
                        p[j++].Value = dtRow["op_status"];                                                      //  6 op_status
                        p[j++].Value = dtRow["alarm_status"];                                                   //  7 alarm_status
                        p[j++].Value = dtRow["warning"];                                                        //  8 warning
                        p[j++].Value = dtRow["fault"];                                                          //  9 fault
                        p[j++].Value = dtRow["module_voltage"];                                                 // 10 module_voltage
                        p[j++].Value = dtRow["module_current"];                                                 // 11 module_current
                        p[j++].Value = dtRow["module_soc"];                                                     // 12 module_soc
                        p[j++].Value = dtRow["module_soh"];                                                     // 13 module_soh

                        for (int i = 0; i < 15; i++)
                        {
                            p[j++].Value = dtRow[String.Format("cell_voltage_{0:00}", i + 1)];                // 14 cell_voltage_01 28
                        }
                        for (int i = 0; i < 6; i++)
                        {
                            p[j++].Value = dtRow[String.Format("temperature_{0:00}", i + 1)];                 // 29 cell_voltage_01 36
                        }

                        p[j++].Value = dtRow["chg_option"];                                                     // 37 chg_option
                        p[j++].Value = dtRow["chg_cal"];                                                        // 38 chg_cal
                        p[j++].Value = dtRow["dch_cal"];                                                        // 39 dch_cal

                        command.ExecuteNonQuery();

                        CommitTran(connection);
                    }
                }
                catch (SQLiteException se)
                {
                    if (se.ResultCode != SQLiteErrorCode.Constraint)
                        result = se.Message;
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
        }

        public static DataTable BmsDataSelectToDataTable(CommConfig aConfig, DateTime aDate, string qry)
        {
            string result = "";
            DataTable dtResult = null;

            string ModelName = csConstData.UART_MODEL[aConfig.UartModelIndex];
            string dbFilePath = GetDbFilePath(ModelName, aDate);

            if (Directory.Exists(System.IO.Path.GetDirectoryName(dbFilePath)) == false)
                return dtResult;
            if (File.Exists(dbFilePath) == false)
                return dtResult;

            string strConn = @"Data Source=" + dbFilePath;
            using (var connection = new SQLiteConnection(strConn))
            {
                try
                {
                    connection.Open();
                    BeginTran(connection);

                    // Insert data
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        //sSQL = "insert into TrendTable ( TrendStamp, TagName, TagValue) Values ( " + IntToStr(stamp) + "," + name + "," + value + ");";
                        command.CommandText = String.Format("SELECT * FROM TModuleValue {0}", qry);
                        SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
                        adapter.AcceptChangesDuringFill = false;
                        dtResult = new DataTable();
                        adapter.Fill(dtResult);
                        dtResult.TableName = "TModuleValue";

                        CommitTran(connection);

                        System.Windows.Forms.Application.DoEvents();
                    }
                }
                catch (Exception e)
                {
                    result = e.Message;
                }
                finally
                {
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    if (result != "") throw new Exception(result);
                }
            }
            return dtResult;
        }
        #endregion

        #region DB PRAGMA FUNCTION
        public static class SqlitePragmaHelper
        {
            /// <summary>
            /// SQLite PRAGMA 설정을 적용합니다.
            /// - forWriter = true : 로그 쓰기 스레드(INSERT)용 프로파일
            /// - forWriter = false: UI 조회(SELECT)용 프로파일
            /// </summary>
            public static void ApplyPragmas(SQLiteConnection conn, bool forWriter)
            {
                // C# 7.3: using(var ...) 패턴 사용
                using (var cmd = conn.CreateCommand())
                {
                    var sb = new StringBuilder();

                    // 공통 권장값
                    sb.AppendLine("PRAGMA journal_mode=WAL;");   // 동시 읽기/쓰기
                    sb.AppendLine("PRAGMA busy_timeout=3000;");  // 잠금 대기(ms)
                    sb.AppendLine("PRAGMA temp_store=MEMORY;");  // 임시 테이블 메모리
                    sb.AppendLine("PRAGMA cache_size=-8000;");   // 8MB(음수=KB)

                    if (forWriter)
                    {
                        // 쓰기 성능/안정성 균형
                        sb.AppendLine("PRAGMA synchronous=NORMAL;");
                        sb.AppendLine("PRAGMA locking_mode=NORMAL;");
                        // sb.AppendLine("PRAGMA foreign_keys=OFF;"); // FK 미사용 시만
                    }
                    else
                    {
                        // 읽기 전용 커넥션(조회)
                        sb.AppendLine("PRAGMA synchronous=OFF;"); // 읽기에는 의미 거의 없음
                        sb.AppendLine("PRAGMA locking_mode=NORMAL;");
                    }

                    cmd.CommandText = sb.ToString();
                    cmd.ExecuteNonQuery();
                }
            }

            /// <summary>
            /// 환경/현장에 맞게 타임아웃/캐시 크기를 조절해서 적용하고 싶을 때.
            /// cacheSizeKB: 음수면 KB 단위(권장), 예: -8000 = 8MB
            /// </summary>
            public static void ApplyPragmas(SQLiteConnection conn, bool forWriter, int busyTimeoutMs, int cacheSizeKB)
            {
                using (var cmd = conn.CreateCommand())
                {
                    var sb = new StringBuilder();

                    sb.AppendLine("PRAGMA journal_mode=WAL;");
                    sb.AppendLine("PRAGMA busy_timeout=" + busyTimeoutMs + ";");
                    sb.AppendLine("PRAGMA temp_store=MEMORY;");
                    sb.AppendLine("PRAGMA cache_size=" + cacheSizeKB + ";");

                    if (forWriter)
                    {
                        sb.AppendLine("PRAGMA synchronous=NORMAL;");
                        sb.AppendLine("PRAGMA locking_mode=NORMAL;");
                    }
                    else
                    {
                        sb.AppendLine("PRAGMA synchronous=OFF;");
                        sb.AppendLine("PRAGMA locking_mode=NORMAL;");
                    }

                    cmd.CommandText = sb.ToString();
                    cmd.ExecuteNonQuery();
                }
            }

            /// <summary>
            /// 현재 PRAGMA 상태를 로깅(디버깅용).
            /// </summary>
            public static void LogPragmas(SQLiteConnection conn, System.Action<string> log)
            {
                var names = new[]
                {
                "journal_mode","synchronous","busy_timeout","temp_store",
                "cache_size","locking_mode","wal_autocheckpoint","page_size","mmap_size"
            };

                using (var cmd = conn.CreateCommand())
                {
                    for (int i = 0; i < names.Length; i++)
                    {
                        cmd.CommandText = "PRAGMA " + names[i] + ";";
                        object val = cmd.ExecuteScalar();
                        if (log != null)
                            log("PRAGMA " + names[i] + " = " + (val ?? "(null)"));
                    }
                }
            }

            /// <summary>
            /// WAL 파일을 DB로 반영하고 WAL 파일을 truncate 합니다.
            /// 프로그램 종료 시 1회 또는 하루 1회 등으로 호출 권장.
            /// </summary>
            public static void WalCheckpointTruncate(SQLiteConnection conn)
            {
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "PRAGMA wal_checkpoint(TRUNCATE);";
                    cmd.ExecuteNonQuery();
                }
            }
        }
        #endregion

        #region GRID VIEW COLUMN HIDE
        // 바인딩 끝난 뒤(예: DataSource 설정 직후)에 실행
        public static void HideColumns(DataGridView grid, params string[] names)
        {
            foreach (var name in names)
            {
                if (grid.Columns.Contains(name))
                    grid.Columns[name].Visible = false;   // 칼럼 자체 숨김
            }
        }
        #endregion

        public class SQLITE
        {
            private SQLiteConnection con;
            private SQLiteCommand cmd;
            private SQLiteDataAdapter adapter;

            public SQLITE(string databasename)
            {
                con = new SQLiteConnection(string.Format("Data Source={0};Compress=True;", databasename));
            }

            public int Execute(string sql_statement)
            {
                con.Open();
                cmd = con.CreateCommand();
                cmd.CommandText = sql_statement;
                int row_updated;
                try
                {
                    row_updated = cmd.ExecuteNonQuery();
                }
                catch
                {
                    con.Close();
                    return 0;
                }
                con.Close();
                return row_updated;
            }
            public DataTable GetDataTable(string tablename)
            {
                DataTable DT = new DataTable();
                con.Open();
                cmd = con.CreateCommand();
                cmd.CommandText = string.Format("SELECT * FROM {0}", tablename);
                adapter = new SQLiteDataAdapter(cmd);
                adapter.AcceptChangesDuringFill = false;
                adapter.Fill(DT);
                con.Close();
                DT.TableName = tablename;
                return DT;
            }
            public void SaveDataTable(DataTable DT)
            {
                try
                {
                    Execute(string.Format("DELETE FROM {0}", DT.TableName));
                    con.Open();
                    cmd = con.CreateCommand();
                    cmd.CommandText = string.Format("SELECT * FROM {0}", DT.TableName);
                    adapter = new SQLiteDataAdapter(cmd);
                    SQLiteCommandBuilder builder = new SQLiteCommandBuilder(adapter);
                    adapter.Update(DT);
                    con.Close();
                }
                catch (Exception Ex)
                {
                    MessageBox.Show(Ex.Message);
                }
            }
        }
    }
}
