using System;
using System.IO;
using System.Text;

namespace Intelix.Helper.Sql;

public class SqLite
{
	private struct RecordHeaderField
	{
		public long Size;

		public long Type;
	}

	private struct TableEntry
	{
		public string[] Content;
	}

	private struct SqliteMasterEntry
	{
		public string ItemName;

		public long RootNum;

		public string SqlStatement;
	}

	private readonly ulong _dbEncoding;

	private readonly byte[] _fileBytes;

	private readonly ulong _pageSize;

	private readonly byte[] _sqlDataTypeSize = new byte[10] { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0 };

	private string[] _fieldNames;

	private SqliteMasterEntry[] _masterTableEntries;

	private TableEntry[] _tableEntries;

	public SqLite(string fileName)
	{
		_fileBytes = File.ReadAllBytes(fileName);
		_pageSize = ConvertToULong(16, 2);
		_dbEncoding = ConvertToULong(56, 4);
		ReadMasterTable(100L);
	}

	public SqLite(byte[] basedata)
	{
		_fileBytes = basedata;
		_pageSize = ConvertToULong(16, 2);
		_dbEncoding = ConvertToULong(56, 4);
		ReadMasterTable(100L);
	}

	public string GetValue(int rowNum, int field)
	{
		try
		{
			if (rowNum >= _tableEntries.Length)
			{
				return null;
			}
			return (field >= _tableEntries[rowNum].Content.Length) ? null : _tableEntries[rowNum].Content[field];
		}
		catch
		{
			return "";
		}
	}

	public int GetRowCount()
	{
		return _tableEntries.Length;
	}

	private bool ReadTableFromOffset(ulong offset)
	{
		try
		{
			switch (_fileBytes[offset])
			{
			case 13:
			{
				uint num4 = (uint)(ConvertToULong((int)offset + 3, 2) - 1);
				int num5 = 0;
				if (_tableEntries != null)
				{
					num5 = _tableEntries.Length;
					Array.Resize(ref _tableEntries, _tableEntries.Length + (int)num4 + 1);
				}
				else
				{
					_tableEntries = new TableEntry[num4 + 1];
				}
				for (uint num6 = 0u; (int)num6 <= (int)num4; num6++)
				{
					ulong num7 = ConvertToULong((int)offset + 8 + (int)(num6 * 2), 2);
					if (offset != 100)
					{
						num7 += offset;
					}
					int num8 = Gvl((int)num7);
					Cvl((int)num7, num8);
					int num9 = Gvl((int)((long)num7 + ((long)num8 - (long)num7) + 1));
					Cvl((int)((long)num7 + ((long)num8 - (long)num7) + 1), num9);
					ulong num10 = num7 + (ulong)((long)num9 - (long)num7 + 1);
					int num11 = Gvl((int)num10);
					int num12 = num11;
					long num13 = Cvl((int)num10, num11);
					RecordHeaderField[] array = null;
					long num14 = (long)num10 - (long)num11 + 1;
					int num15 = 0;
					while (num14 < num13)
					{
						Array.Resize(ref array, num15 + 1);
						int num16 = num12 + 1;
						num12 = Gvl(num16);
						array[num15].Type = Cvl(num16, num12);
						array[num15].Size = ((array[num15].Type <= 9) ? _sqlDataTypeSize[array[num15].Type] : ((!IsOdd(array[num15].Type)) ? ((array[num15].Type - 12) / 2) : ((array[num15].Type - 13) / 2)));
						num14 = num14 + (num12 - num16) + 1;
						num15++;
					}
					if (array == null)
					{
						continue;
					}
					_tableEntries[num5 + (int)num6].Content = new string[array.Length];
					int num17 = 0;
					for (int i = 0; i <= array.Length - 1; i++)
					{
						if (array[i].Type > 9)
						{
							if (!IsOdd(array[i].Type))
							{
								long dbEncoding = (long)_dbEncoding;
								long num18 = dbEncoding - 1;
								if ((ulong)num18 <= 2uL)
								{
									switch (num18)
									{
									case 0L:
										_tableEntries[num5 + (int)num6].Content[i] = Encoding.Default.GetString(_fileBytes, (int)((long)num10 + num13 + num17), (int)array[i].Size);
										break;
									case 1L:
										_tableEntries[num5 + (int)num6].Content[i] = Encoding.Unicode.GetString(_fileBytes, (int)((long)num10 + num13 + num17), (int)array[i].Size);
										break;
									case 2L:
										_tableEntries[num5 + (int)num6].Content[i] = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num10 + num13 + num17), (int)array[i].Size);
										break;
									}
								}
							}
							else
							{
								_tableEntries[num5 + (int)num6].Content[i] = Encoding.Default.GetString(_fileBytes, (int)((long)num10 + num13 + num17), (int)array[i].Size);
							}
						}
						else
						{
							_tableEntries[num5 + (int)num6].Content[i] = Convert.ToString(ConvertToULong((int)((long)num10 + num13 + num17), (int)array[i].Size));
						}
						num17 += (int)array[i].Size;
					}
				}
				break;
			}
			case 5:
			{
				uint num = (uint)(ConvertToULong((int)(offset + 3), 2) - 1);
				for (uint num2 = 0u; (int)num2 <= (int)num; num2++)
				{
					uint num3 = (uint)ConvertToULong((int)offset + 12 + (int)(num2 * 2), 2);
					ReadTableFromOffset((ConvertToULong((int)(offset + num3), 4) - 1) * _pageSize);
				}
				ReadTableFromOffset((ConvertToULong((int)(offset + 8), 4) - 1) * _pageSize);
				break;
			}
			}
			return true;
		}
		catch
		{
			return false;
		}
	}

	private void ReadMasterTable(long offset)
	{
		while (true)
		{
			switch (_fileBytes[offset])
			{
			default:
				return;
			case 5:
			{
				uint num13 = (uint)(ConvertToULong((int)offset + 3, 2) - 1);
				for (int j = 0; j <= (int)num13; j++)
				{
					uint num14 = (uint)ConvertToULong((int)offset + 12 + j * 2, 2);
					if (offset == 100)
					{
						ReadMasterTable((long)((ConvertToULong((int)num14, 4) - 1) * _pageSize));
					}
					else
					{
						ReadMasterTable((long)((ConvertToULong((int)(offset + num14), 4) - 1) * _pageSize));
					}
				}
				break;
			}
			case 13:
			{
				ulong num = ConvertToULong((int)offset + 3, 2) - 1;
				int num2 = 0;
				if (_masterTableEntries != null)
				{
					num2 = _masterTableEntries.Length;
					Array.Resize(ref _masterTableEntries, _masterTableEntries.Length + (int)num + 1);
				}
				else
				{
					checked
					{
						_masterTableEntries = new SqliteMasterEntry[(ulong)unchecked((long)(num + 1))];
					}
				}
				for (ulong num3 = 0uL; num3 <= num; num3++)
				{
					ulong num4 = ConvertToULong((int)offset + 8 + (int)num3 * 2, 2);
					if (offset != 100)
					{
						num4 += (ulong)offset;
					}
					int num5 = Gvl((int)num4);
					Cvl((int)num4, num5);
					int num6 = Gvl((int)((long)num4 + ((long)num5 - (long)num4) + 1));
					Cvl((int)((long)num4 + ((long)num5 - (long)num4) + 1), num6);
					ulong num7 = num4 + (ulong)((long)num6 - (long)num4 + 1);
					int num8 = Gvl((int)num7);
					int num9 = num8;
					long num10 = Cvl((int)num7, num8);
					long[] array = new long[5];
					for (int i = 0; i <= 4; i++)
					{
						int startIdx = num9 + 1;
						num9 = Gvl(startIdx);
						array[i] = Cvl(startIdx, num9);
						array[i] = ((array[i] <= 9) ? _sqlDataTypeSize[array[i]] : ((!IsOdd(array[i])) ? ((array[i] - 12) / 2) : ((array[i] - 13) / 2)));
					}
					long dbEncoding;
					if (_dbEncoding == 1 || _dbEncoding == 2)
					{
						dbEncoding = (long)_dbEncoding;
						long num11 = dbEncoding - 1;
						if ((ulong)num11 <= 2uL)
						{
							switch (num11)
							{
							case 0L:
								_masterTableEntries[num2 + (int)num3].ItemName = Encoding.Default.GetString(_fileBytes, (int)((long)num7 + num10 + array[0]), (int)array[1]);
								break;
							case 1L:
								_masterTableEntries[num2 + (int)num3].ItemName = Encoding.Unicode.GetString(_fileBytes, (int)((long)num7 + num10 + array[0]), (int)array[1]);
								break;
							case 2L:
								_masterTableEntries[num2 + (int)num3].ItemName = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num7 + num10 + array[0]), (int)array[1]);
								break;
							}
						}
					}
					_masterTableEntries[num2 + (int)num3].RootNum = (long)ConvertToULong((int)((long)num7 + num10 + array[0] + array[1] + array[2]), (int)array[3]);
					dbEncoding = (long)_dbEncoding;
					long num12 = dbEncoding - 1;
					if ((ulong)num12 <= 2uL)
					{
						switch (num12)
						{
						case 0L:
							_masterTableEntries[num2 + (int)num3].SqlStatement = Encoding.Default.GetString(_fileBytes, (int)((long)num7 + num10 + array[0] + array[1] + array[2] + array[3]), (int)array[4]);
							break;
						case 1L:
							_masterTableEntries[num2 + (int)num3].SqlStatement = Encoding.Unicode.GetString(_fileBytes, (int)((long)num7 + num10 + array[0] + array[1] + array[2] + array[3]), (int)array[4]);
							break;
						case 2L:
							_masterTableEntries[num2 + (int)num3].SqlStatement = Encoding.BigEndianUnicode.GetString(_fileBytes, (int)((long)num7 + num10 + array[0] + array[1] + array[2] + array[3]), (int)array[4]);
							break;
						}
					}
				}
				return;
			}
			}
			offset = (long)((ConvertToULong((int)offset + 8, 4) - 1) * _pageSize);
		}
	}

	public bool ReadTable(string tableName)
	{
		int num = -1;
		for (int i = 0; i <= _masterTableEntries.Length; i++)
		{
			if (string.Compare(_masterTableEntries[i].ItemName.ToLower(), tableName.ToLower(), StringComparison.Ordinal) == 0)
			{
				num = i;
				break;
			}
		}
		if (num == -1)
		{
			return false;
		}
		string[] array = _masterTableEntries[num].SqlStatement.Substring(_masterTableEntries[num].SqlStatement.IndexOf("(", StringComparison.Ordinal) + 1).Split(',');
		for (int j = 0; j <= array.Length - 1; j++)
		{
			array[j] = array[j].TrimStart();
			int num2 = array[j].IndexOf(' ');
			if (num2 > 0)
			{
				array[j] = array[j].Substring(0, num2);
			}
			if (array[j].IndexOf("UNIQUE", StringComparison.Ordinal) != 0)
			{
				Array.Resize(ref _fieldNames, j + 1);
				_fieldNames[j] = array[j];
			}
		}
		return ReadTableFromOffset((ulong)(_masterTableEntries[num].RootNum - 1) * _pageSize);
	}

	private ulong ConvertToULong(int startIndex, int size)
	{
		try
		{
			if (size > 8 || size == 0)
			{
				return 0uL;
			}
			ulong num = 0uL;
			for (int i = 0; i <= size - 1; i++)
			{
				num = (num << 8) | _fileBytes[startIndex + i];
			}
			return num;
		}
		catch
		{
			return 0uL;
		}
	}

	private int Gvl(int startIdx)
	{
		try
		{
			if (startIdx > _fileBytes.Length)
			{
				return 0;
			}
			for (int i = startIdx; i <= startIdx + 8; i++)
			{
				if (i > _fileBytes.Length - 1)
				{
					return 0;
				}
				if ((_fileBytes[i] & 0x80) != 128)
				{
					return i;
				}
			}
			return startIdx + 8;
		}
		catch
		{
			return 0;
		}
	}

	private long Cvl(int startIdx, int endIdx)
	{
		try
		{
			endIdx++;
			byte[] array = new byte[8];
			int num = endIdx - startIdx;
			bool flag = false;
			if (num == 0 || num > 9)
			{
				return 0L;
			}
			switch (num)
			{
			case 1:
				array[0] = (byte)(_fileBytes[startIdx] & 0x7F);
				return BitConverter.ToInt64(array, 0);
			case 9:
				flag = true;
				break;
			}
			int num2 = 1;
			int num3 = 7;
			int num4 = 0;
			if (flag)
			{
				array[0] = _fileBytes[endIdx - 1];
				endIdx--;
				num4 = 1;
			}
			for (int i = endIdx - 1; i >= startIdx; i += -1)
			{
				if (i - 1 >= startIdx)
				{
					array[num4] = (byte)(((_fileBytes[i] >> num2 - 1) & (255 >> num2)) | (_fileBytes[i - 1] << num3));
					num2++;
					num4++;
					num3--;
				}
				else if (!flag)
				{
					array[num4] = (byte)((_fileBytes[i] >> num2 - 1) & (255 >> num2));
				}
			}
			return BitConverter.ToInt64(array, 0);
		}
		catch
		{
			return 0L;
		}
	}

	private static bool IsOdd(long value)
	{
		return (value & 1) == 1;
	}

	public static SqLite ReadTable(string database, string table)
	{
		try
		{
			string text = Path.GetTempFileName() + ".tmpdb";
			File.Copy(database, text);
			SqLite sqLite = new SqLite(text);
			sqLite.ReadTable(table);
			File.Delete(text);
			return (sqLite.GetRowCount() == 65536) ? null : sqLite;
		}
		catch
		{
			return null;
		}
	}
}
