using Informix.Net.Core;
using Microsoft.Win32;
using System;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            PrepareInformixFiles();

            if (IsAdministrator() == true)
            {
                PrepareInformixRegistry("informix", "127.0.0.1", 9088);
            }
            else
            {
                Console.WriteLine("At least once, you have to run this program as administrator for setting up registry");
            }

            string connectionString = "Server=informix; User ID=informix; Password=in4mix";

            CreateSampleDB(connectionString);

            TouchDatabase(connectionString);
        }

        private static void PrepareInformixRegistry(string dbInstanceName, string ipOrDns, int port)
        {
            if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                string exepath = typeof(Program).Assembly.Location;
                string path = Path.GetDirectoryName(exepath);

                string rootPath = @"SOFTWARE\Informix";

                RegistryKey regKey = Registry.LocalMachine.OpenSubKey(rootPath);
                if (regKey != null)
                {
                    return;
                }

                regKey = Registry.LocalMachine.CreateSubKey(rootPath, true);

                {
                    RegistryKey envKey = regKey.CreateSubKey("Environment", true);
                    envKey.SetValue("INFORMIXDIR", path);
                }

                {
                    RegistryKey instanceKey = regKey.CreateSubKey(@$"SqlHosts\{dbInstanceName}");
                    instanceKey.SetValue("HOST", ipOrDns);
                    instanceKey.SetValue("SERVICE", $"{port}");
                    instanceKey.SetValue("PROTOCOL", "onsoctcp");
                }
            }
        }

        private static void PrepareInformixFiles()
        {
            if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                string exepath = typeof(Program).Assembly.Location;
                string path = Path.GetDirectoryName(exepath);
                string flagPath = Path.Combine(path, "gls");
                if (Directory.Exists(flagPath))
                {
                    return;
                }

                string zipPath = Path.Combine(path, "informix_binaries.zip");

                var archive = ZipFile.OpenRead(zipPath);
                archive.ExtractToDirectory(path);
            }
        }

        private static void TouchDatabase(string connectionString)
        {
            using (IfxConnection connection = new IfxConnection($"{connectionString}; Database=mydb"))
            {
                connection.ConnectionString = connectionString;
                connection.Open();

                // Create
                IfxCommand insertCommand = new IfxCommand();
                insertCommand.Connection = connection;
                insertCommand.CommandText = "insert into mytable(name, age) values (?, ?);";
                insertCommand.Parameters.Add("@name", IfxType.VarChar, 50);
                insertCommand.Parameters.Add("@age", IfxType.Integer);

                string nameValue = "Name" + Guid.NewGuid().ToString();
                insertCommand.Parameters[0].Value = nameValue;
                insertCommand.Parameters[1].Value = 10;

                int affected = insertCommand.ExecuteNonQuery();
                Console.WriteLine("# of affected row: " + affected);

                // Update
                IfxCommand updateCommand = new IfxCommand();
                updateCommand.Connection = connection;
                updateCommand.CommandText = "UPDATE mytable SET age=? WHERE name=?";

                updateCommand.Parameters.Add("@age", IfxType.Integer);
                updateCommand.Parameters.Add("@name", IfxType.VarChar, 50);

                updateCommand.Parameters[0].Value = 6;
                updateCommand.Parameters[1].Value = nameValue;

                affected = updateCommand.ExecuteNonQuery();
                Console.WriteLine("# of affected row: " + affected);

                // Select - ExecuteScalar
                IfxCommand selectCommand = new IfxCommand();
                selectCommand.Connection = connection;
                selectCommand.CommandText = "SELECT count(*) FROM mytable";

                object result = selectCommand.ExecuteScalar();
                Console.WriteLine("# of records: " + result);

                // Select - DataTable
                DataSet ds = new DataSet();
                IfxDataAdapter da = new IfxDataAdapter("SELECT * FROM mytable", connection);
                da.Fill(ds, "mytable");

                DataTable dt = ds.Tables["mytable"];
                foreach (DataRow dr in dt.Rows)
                {
                    Console.WriteLine(string.Format("Name = {0}, Desc = {1}", dr["name"], dr["age"]));
                }

                // Delete
                IfxCommand deleteCommand = new IfxCommand();
                deleteCommand.Connection = connection;
                deleteCommand.CommandText = "DELETE FROM mytable WHERE name=?";

                deleteCommand.Parameters.Add("@name", IfxType.VarChar, 50);
                deleteCommand.Parameters[0].Value = nameValue;

                affected = deleteCommand.ExecuteNonQuery();
                Console.WriteLine("# of affected row: " + affected);
            }
        }

        private static void CreateSampleDB(string connectionString)
        {
            IfxConnection conn = new IfxConnection(connectionString);
            conn.Open();

            IfxCommand cmd = new IfxCommand("CREATE DATABASE IF NOT EXISTS mydb");
            cmd.Connection = conn;
            cmd.ExecuteNonQuery();

            cmd.CommandText = @"CREATE TABLE IF NOT EXISTS mytable
               (
                   id  SERIAL NOT NULL PRIMARY KEY,
                   name    VARCHAR(50),
                   age      INTEGER
               );";
            cmd.ExecuteNonQuery();

            conn.Close();
        }

        // https://www.sysnet.pe.kr/2/0/950
        public static bool IsAdministrator()
        {
            if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                WindowsIdentity identity = WindowsIdentity.GetCurrent();

                if (null != identity)
                {
                    WindowsPrincipal principal = new WindowsPrincipal(identity);
                    return principal.IsInRole(WindowsBuiltInRole.Administrator);
                }
            }

            return false;
        }
    }
}
