using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace SHARP
{
    public class SQLiteHandler
    {
      private readonly byte[] db_bytes;
      private readonly ulong encoding;
      private string[] field_names = new string[1];
      private SQLiteHandler.sqlite_master_entry[] master_table_entries;
      private readonly ushort page_size;
      private readonly byte[] SQLDataTypeSize = new byte[10]
      {
        (byte) 0,
        (byte) 1,
        (byte) 2,
        (byte) 3,
        (byte) 4,
        (byte) 6,
        (byte) 8,
        (byte) 8,
        (byte) 0,
        (byte) 0
      };
      private SQLiteHandler.table_entry[] table_entries;

      public SQLiteHandler(string baseName)
      {
        if (!File.Exists(baseName))
          return;
        this.db_bytes = File.ReadAllBytes(baseName);
        if (Encoding.Default.GetString(this.db_bytes, 0, 15).CompareTo("SQLite format 3") != 0)
          throw new Exception("Not a valid SQLite 3 Database File");
        if (this.db_bytes[52] != (byte) 0)
          throw new Exception("Auto-vacuum capable database is not supported");
        this.page_size = (ushort) this.ConvertToInteger(16 /*0x10*/, 2);
        this.encoding = this.ConvertToInteger(56, 4);
        if (Decimal.Compare(new Decimal(this.encoding), 0M) == 0)
          this.encoding = 1UL;
        this.ReadMasterTable(100UL);
      }

      private ulong ConvertToInteger(int startIndex, int Size)
      {
        if (Size > 8 | Size == 0)
          return 0;
        ulong integer = 0;
        int num = Size - 1;
        for (int index = 0; index <= num; ++index)
          integer = integer << 8 | (ulong) this.db_bytes[startIndex + index];
        return integer;
      }

      private long CVL(int startIndex, int endIndex)
      {
        ++endIndex;
        byte[] numArray = new byte[8];
        int num1 = endIndex - startIndex;
        bool flag = false;
        if (num1 == 0 | num1 > 9)
          return 0;
        switch (num1)
        {
          case 1:
            numArray[0] = (byte) ((uint) this.db_bytes[startIndex] & (uint) sbyte.MaxValue);
            return BitConverter.ToInt64(numArray, 0);
          case 9:
            flag = true;
            break;
        }
        int num2 = 1;
        int num3 = 7;
        int index1 = 0;
        if (flag)
        {
          numArray[0] = this.db_bytes[endIndex - 1];
          --endIndex;
          index1 = 1;
        }
        int num4 = startIndex;
        for (int index2 = endIndex - 1; index2 >= num4; index2 += -1)
        {
          if (index2 - 1 >= startIndex)
          {
            numArray[index1] = (byte) ((uint) (byte) ((uint) this.db_bytes[index2] >> (num2 - 1 & 7)) & (uint) ((int) byte.MaxValue >> num2) | (uint) (byte) ((uint) this.db_bytes[index2 - 1] << (num3 & 7)));
            ++num2;
            ++index1;
            --num3;
          }
          else if (!flag)
            numArray[index1] = (byte) ((uint) (byte) ((uint) this.db_bytes[index2] >> (num2 - 1 & 7)) & (uint) ((int) byte.MaxValue >> num2));
        }
        return BitConverter.ToInt64(numArray, 0);
      }

      public int GetRowCount() => this.table_entries.Length;

      public string[] GetTableNames()
      {
        List<string> stringList = new List<string>();
        int num = this.master_table_entries.Length - 1;
        for (int index = 0; index <= num; ++index)
        {
          if (this.master_table_entries[index].item_type == "table")
            stringList.Add(this.master_table_entries[index].item_name);
        }
        return stringList.ToArray();
      }

      public string GetValue(int row_num, int field)
      {
        if (row_num >= this.table_entries.Length)
          return (string) null;
        return field >= this.table_entries[row_num].content.Length ? (string) null : this.table_entries[row_num].content[field];
      }

      public string GetValue(int row_num, string field)
      {
        int field1 = -1;
        int num = this.field_names.Length - 1;
        for (int index = 0; index <= num; ++index)
        {
          if (this.field_names[index].ToLower().CompareTo(field.ToLower()) == 0)
          {
            field1 = index;
            break;
          }
        }
        return field1 == -1 ? (string) null : this.GetValue(row_num, field1);
      }

      private int GVL(int startIndex)
      {
        if (startIndex > this.db_bytes.Length)
          return 0;
        int num = startIndex + 8;
        for (int index = startIndex; index <= num; ++index)
        {
          if (index > this.db_bytes.Length - 1)
            return 0;
          if (((int) this.db_bytes[index] & 128 /*0x80*/) != 128 /*0x80*/)
            return index;
        }
        return startIndex + 8;
      }

      private bool IsOdd(long value) => (value & 1L) == 1L;

      private void ReadMasterTable(ulong Offset)
      {
        if (this.db_bytes[(int) Offset] == (byte) 13)
        {
          ushort uint16 = Convert.ToUInt16(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 3M)), 2)), 1M));
          int num1 = 0;
          if (this.master_table_entries != null)
          {
            num1 = this.master_table_entries.Length;
            Array.Resize<SQLiteHandler.sqlite_master_entry>(ref this.master_table_entries, this.master_table_entries.Length + (int) uint16 + 1);
          }
          else
            this.master_table_entries = new SQLiteHandler.sqlite_master_entry[(int) uint16 + 1];
          int num2 = (int) uint16;
          for (int index1 = 0; index1 <= num2; ++index1)
          {
            ulong integer = this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(Offset), 8M), new Decimal(index1 * 2))), 2);
            if (Decimal.Compare(new Decimal(Offset), 100M) != 0)
              integer += Offset;
            int endIndex1 = this.GVL((int) integer);
            this.CVL((int) integer, endIndex1);
            int endIndex2 = this.GVL(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex1), new Decimal(integer))), 1M)));
            this.master_table_entries[num1 + index1].row_id = this.CVL(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex1), new Decimal(integer))), 1M)), endIndex2);
            ulong uint64 = Convert.ToUInt64(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex2), new Decimal(integer))), 1M));
            int endIndex3 = this.GVL((int) uint64);
            int endIndex4 = endIndex3;
            long num3 = this.CVL((int) uint64, endIndex3);
            long[] numArray = new long[5];
            int index2 = 0;
            do
            {
              int startIndex = endIndex4 + 1;
              endIndex4 = this.GVL(startIndex);
              numArray[index2] = this.CVL(startIndex, endIndex4);
              numArray[index2] = numArray[index2] <= 9L ? (long) this.SQLDataTypeSize[(int) numArray[index2]] : (!this.IsOdd(numArray[index2]) ? (long) Math.Round((double) (numArray[index2] - 12L) / 2.0) : (long) Math.Round((double) (numArray[index2] - 13L) / 2.0));
              ++index2;
            }
            while (index2 <= 4);
            if (Decimal.Compare(new Decimal(this.encoding), 1M) == 0)
              this.master_table_entries[num1 + index1].item_type = Encoding.Default.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(new Decimal(uint64), new Decimal(num3))), (int) numArray[0]);
            else if (Decimal.Compare(new Decimal(this.encoding), 2M) == 0)
              this.master_table_entries[num1 + index1].item_type = Encoding.Unicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(new Decimal(uint64), new Decimal(num3))), (int) numArray[0]);
            else if (Decimal.Compare(new Decimal(this.encoding), 3M) == 0)
              this.master_table_entries[num1 + index1].item_type = Encoding.BigEndianUnicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(new Decimal(uint64), new Decimal(num3))), (int) numArray[0]);
            if (Decimal.Compare(new Decimal(this.encoding), 1M) == 0)
              this.master_table_entries[num1 + index1].item_name = Encoding.Default.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0]))), (int) numArray[1]);
            else if (Decimal.Compare(new Decimal(this.encoding), 2M) == 0)
              this.master_table_entries[num1 + index1].item_name = Encoding.Unicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0]))), (int) numArray[1]);
            else if (Decimal.Compare(new Decimal(this.encoding), 3M) == 0)
              this.master_table_entries[num1 + index1].item_name = Encoding.BigEndianUnicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0]))), (int) numArray[1]);
            this.master_table_entries[num1 + index1].root_num = (long) this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0])), new Decimal(numArray[1])), new Decimal(numArray[2]))), (int) numArray[3]);
            if (Decimal.Compare(new Decimal(this.encoding), 1M) == 0)
              this.master_table_entries[num1 + index1].sql_statement = Encoding.Default.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0])), new Decimal(numArray[1])), new Decimal(numArray[2])), new Decimal(numArray[3]))), (int) numArray[4]);
            else if (Decimal.Compare(new Decimal(this.encoding), 2M) == 0)
              this.master_table_entries[num1 + index1].sql_statement = Encoding.Unicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0])), new Decimal(numArray[1])), new Decimal(numArray[2])), new Decimal(numArray[3]))), (int) numArray[4]);
            else if (Decimal.Compare(new Decimal(this.encoding), 3M) == 0)
              this.master_table_entries[num1 + index1].sql_statement = Encoding.BigEndianUnicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(numArray[0])), new Decimal(numArray[1])), new Decimal(numArray[2])), new Decimal(numArray[3]))), (int) numArray[4]);
          }
        }
        else
        {
          if (this.db_bytes[(int) Offset] != (byte) 5)
            return;
          int uint16 = (int) Convert.ToUInt16(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 3M)), 2)), 1M));
          for (int index = 0; index <= uint16; ++index)
          {
            ushort integer = (ushort) this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(Offset), 12M), new Decimal(index * 2))), 2);
            if (Decimal.Compare(new Decimal(Offset), 100M) == 0)
              this.ReadMasterTable(Convert.ToUInt64(Decimal.Multiply(Decimal.Subtract(new Decimal(this.ConvertToInteger((int) integer, 4)), 1M), new Decimal((int) this.page_size))));
            else
              this.ReadMasterTable(Convert.ToUInt64(Decimal.Multiply(Decimal.Subtract(new Decimal(this.ConvertToInteger((int) ((long) Offset + (long) integer), 4)), 1M), new Decimal((int) this.page_size))));
          }
          this.ReadMasterTable(Convert.ToUInt64(Decimal.Multiply(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 8M)), 4)), 1M), new Decimal((int) this.page_size))));
        }
      }

      public bool ReadTable(string TableName)
      {
        int index1 = -1;
        int num1 = this.master_table_entries.Length - 1;
        for (int index2 = 0; index2 <= num1; ++index2)
        {
          if (this.master_table_entries[index2].item_name.ToLower().CompareTo(TableName.ToLower()) == 0)
          {
            index1 = index2;
            break;
          }
        }
        if (index1 == -1)
          return false;
        string[] strArray = this.master_table_entries[index1].sql_statement.Substring(this.master_table_entries[index1].sql_statement.IndexOf("(") + 1).Split(',');
        int num2 = strArray.Length - 1;
        for (int index3 = 0; index3 <= num2; ++index3)
        {
          strArray[index3] = strArray[index3].TrimStart();
          int length = strArray[index3].IndexOf(" ");
          if (length > 0)
            strArray[index3] = strArray[index3].Substring(0, length);
          if (strArray[index3].IndexOf("UNIQUE") != 0)
          {
            Array.Resize<string>(ref this.field_names, index3 + 1);
            this.field_names[index3] = strArray[index3];
          }
          else
            break;
        }
        return this.ReadTableFromOffset((ulong) (this.master_table_entries[index1].root_num - 1L) * (ulong) this.page_size);
      }

      private bool ReadTableFromOffset(ulong Offset)
      {
        if (this.db_bytes[(int) Offset] == (byte) 13)
        {
          int int32 = Convert.ToInt32(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 3M)), 2)), 1M));
          int num1 = 0;
          if (this.table_entries != null)
          {
            num1 = this.table_entries.Length;
            Array.Resize<SQLiteHandler.table_entry>(ref this.table_entries, this.table_entries.Length + int32 + 1);
          }
          else
            this.table_entries = new SQLiteHandler.table_entry[int32 + 1];
          int num2 = int32;
          for (int index1 = 0; index1 <= num2; ++index1)
          {
            SQLiteHandler.record_header_field[] array = new SQLiteHandler.record_header_field[1];
            ulong integer = this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(Offset), 8M), new Decimal(index1 * 2))), 2);
            if (Decimal.Compare(new Decimal(Offset), 100M) != 0)
              integer += Offset;
            int endIndex1 = this.GVL((int) integer);
            this.CVL((int) integer, endIndex1);
            int endIndex2 = this.GVL(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex1), new Decimal(integer))), 1M)));
            this.table_entries[num1 + index1].row_id = this.CVL(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex1), new Decimal(integer))), 1M)), endIndex2);
            ulong uint64 = Convert.ToUInt64(Decimal.Add(Decimal.Add(new Decimal(integer), Decimal.Subtract(new Decimal(endIndex2), new Decimal(integer))), 1M));
            int endIndex3 = this.GVL((int) uint64);
            int endIndex4 = endIndex3;
            long num3 = this.CVL((int) uint64, endIndex3);
            long num4 = Convert.ToInt64(Decimal.Add(Decimal.Subtract(new Decimal(uint64), new Decimal(endIndex3)), 1M));
            int index2 = 0;
            while (num4 < num3)
            {
              Array.Resize<SQLiteHandler.record_header_field>(ref array, index2 + 1);
              int startIndex = endIndex4 + 1;
              endIndex4 = this.GVL(startIndex);
              array[index2].type = this.CVL(startIndex, endIndex4);
              array[index2].size = array[index2].type <= 9L ? (long) this.SQLDataTypeSize[(int) array[index2].type] : (!this.IsOdd(array[index2].type) ? (long) Math.Round((double) (array[index2].type - 12L) / 2.0) : (long) Math.Round((double) (array[index2].type - 13L) / 2.0));
              num4 = num4 + (long) (endIndex4 - startIndex) + 1L;
              ++index2;
            }
            this.table_entries[num1 + index1].content = new string[array.Length - 1 + 1];
            int num5 = 0;
            int num6 = array.Length - 1;
            for (int index3 = 0; index3 <= num6; ++index3)
            {
              if (array[index3].type > 9L)
              {
                if (!this.IsOdd(array[index3].type))
                {
                  if (Decimal.Compare(new Decimal(this.encoding), 1M) == 0)
                    this.table_entries[num1 + index1].content[index3] = Encoding.Default.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(num5))), (int) array[index3].size);
                  else if (Decimal.Compare(new Decimal(this.encoding), 2M) == 0)
                    this.table_entries[num1 + index1].content[index3] = Encoding.Unicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(num5))), (int) array[index3].size);
                  else if (Decimal.Compare(new Decimal(this.encoding), 3M) == 0)
                    this.table_entries[num1 + index1].content[index3] = Encoding.BigEndianUnicode.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(num5))), (int) array[index3].size);
                }
                else
                  this.table_entries[num1 + index1].content[index3] = Encoding.Default.GetString(this.db_bytes, Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(num5))), (int) array[index3].size);
              }
              else
                this.table_entries[num1 + index1].content[index3] = Convert.ToString(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(uint64), new Decimal(num3)), new Decimal(num5))), (int) array[index3].size));
              num5 += (int) array[index3].size;
            }
          }
        }
        else if (this.db_bytes[(int) Offset] == (byte) 5)
        {
          int uint16 = (int) Convert.ToUInt16(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 3M)), 2)), 1M));
          for (int index = 0; index <= uint16; ++index)
          {
            ushort integer = (ushort) this.ConvertToInteger(Convert.ToInt32(Decimal.Add(Decimal.Add(new Decimal(Offset), 12M), new Decimal(index * 2))), 2);
            this.ReadTableFromOffset(Convert.ToUInt64(Decimal.Multiply(Decimal.Subtract(new Decimal(this.ConvertToInteger((int) ((long) Offset + (long) integer), 4)), 1M), new Decimal((int) this.page_size))));
          }
          this.ReadTableFromOffset(Convert.ToUInt64(Decimal.Multiply(Decimal.Subtract(new Decimal(this.ConvertToInteger(Convert.ToInt32(Decimal.Add(new Decimal(Offset), 8M)), 4)), 1M), new Decimal((int) this.page_size))));
        }
        return true;
      }

      private struct record_header_field
      {
        public long size;
        public long type;
      }

      private struct sqlite_master_entry
      {
        public long row_id;
        public string item_type;
        public string item_name;
        public readonly string astable_name;
        public long root_num;
        public string sql_statement;
      }

      private struct table_entry
      {
        public long row_id;
        public string[] content;
      }
    }
}
