﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace dnorml
{
    public static class Cache
    {
        private static readonly Dictionary<string, JObject> Instance =
            new Dictionary<string, JObject>();


        // this index is an optimzation to help us quickly get back to the table given its id column names
        private static readonly Dictionary<string, Table> LookupIdIndex = new Dictionary<string, Table>();

        public static void Set(string environment, string table, string id, JObject obj)
        {            
            Instance[environment + "/" + table + "/" + id] = obj;
        }

        public static JObject Get(string environment, string table, string id)
        {
            string key = environment + "/" + table + "/" + id;
            if (!Instance.ContainsKey(key))
            {
                //Console.WriteLine("WARNING: Missed expected cached data: '{0}'", key);
                return null;
            }
            return Instance[key];
        }

        public static void Init(IDataAccess dal)
        {
            var tables = dal.GetAllTables();

            foreach (var t in tables.Where(t => t.Prefetch))
            {
                Fill(t);
            }
        }

        private static void Fill(Table t)
        {            
            int count = 0;

            LookupIdIndex[t.KeyColumns] = t;
            
            using (var extractConnection = new SqlConnection(t.ExtractConnectionString))
            {
                extractConnection.Open();
				string selectAllCommand = !string.IsNullOrEmpty(t.CustomSql)
					? t.CustomSql
						: String.Format("select * from {0} with(nolock)", t.Name);
                using (var cmd =
                    new SqlCommand(selectAllCommand, extractConnection))
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        var fieldNames = new List<string>();
                        for (int i = 0; i < reader.FieldCount; i++)
                            fieldNames.Add(reader.GetName(i));

                        while (reader.Read())
                        {
                            count++;
                            Dictionary<string, object> changedRowDict = fieldNames.ToDictionary(col => col,
                                                                                                 col => reader[col]);
							var jobj = JObject.Parse (JsonConvert.SerializeObject (changedRowDict));
							Set(t.Environment, t.Name, t.GetKey(jobj), jobj);
                        }
                    }
                }
            }
            Console.WriteLine("Cached {0} rows from {1}.", count, t);
        }

        public static JObject AddLookupData(string environment, JObject o)
        {
            var props = o.Properties().ToList();
            foreach (var p in props)
            {
                foreach (var k in LookupIdIndex.Keys)
                {
                    if (p.Name == k && p.Value != null && p.Value.ToString() != string.Empty)
                    {
                        Table lookupTable = LookupIdIndex[k];
                        
                        JObject lookupItem = Get(environment, lookupTable.Name, p.Value.ToString());

                        if (lookupItem != null)
                        {
                            if (lookupTable.LookupDescriptor != null)
                            {
                                o[lookupTable.Name] = lookupItem[lookupTable.LookupDescriptor];
                            }
                            else
                            {
                                o[lookupTable.Name] = lookupItem;
                            }

                            o.Remove(p.Name);
                        }
                    }
                }                
            }

            return o;
        }
    }
}
