/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using Framework.Database;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Game.DataStorage
{
    public class ConversationDataStorage : Singleton<ConversationDataStorage>
    {
        ConversationDataStorage() { }

        public void LoadConversationTemplates()
        {
            _conversationActorTemplateStorage.Clear();
            _conversationLineTemplateStorage.Clear();
            _conversationTemplateStorage.Clear();

            Dictionary<uint, ConversationActorTemplate[]> actorsByConversation = new Dictionary<uint, ConversationActorTemplate[]>();

            SQLResult actorTemplates = DB.World.Query("SELECT Id, CreatureId, CreatureModelId FROM conversation_actor_template");
            if (!actorTemplates.IsEmpty())
            {
                uint oldMSTime = Time.GetMSTime();

                do
                {
                    uint id = actorTemplates.Read<uint>(0);
                    ConversationActorTemplate conversationActor = new ConversationActorTemplate();
                    conversationActor.Id = id;
                    conversationActor.CreatureId = actorTemplates.Read<uint>(1);
                    conversationActor.CreatureModelId = actorTemplates.Read<uint>(2);

                    _conversationActorTemplateStorage[id] = conversationActor;
                }
                while (actorTemplates.NextRow());

                Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Conversation actor templates in {1} ms", _conversationActorTemplateStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
            }
            else
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation actor templates. DB table `conversation_actor_template` is empty.");
            }

            SQLResult lineTemplates = DB.World.Query("SELECT Id, StartTime, UiCameraID, ActorIdx, Unk FROM conversation_line_template");
            if (!lineTemplates.IsEmpty())
            {
                uint oldMSTime = Time.GetMSTime();

                do
                {
                    uint id = lineTemplates.Read<uint>(0);

                    if (!CliDB.ConversationLineStorage.ContainsKey(id))
                    {
                        Log.outError(LogFilter.Sql, "Table `conversation_line_template` has template for non existing ConversationLine (ID: {0}), skipped", id);
                        continue;
                    }

                    ConversationLineTemplate conversationLine = new ConversationLineTemplate();
                    conversationLine.Id = id;
                    conversationLine.StartTime = lineTemplates.Read<uint>(1);
                    conversationLine.UiCameraID = lineTemplates.Read<uint>(2);
                    conversationLine.ActorIdx = lineTemplates.Read<ushort>(3);
                    conversationLine.Unk = lineTemplates.Read<ushort>(4);

                    _conversationLineTemplateStorage[id] = conversationLine;
                }
                while (lineTemplates.NextRow());

                Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Conversation line templates in {1} ms", _conversationLineTemplateStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
            }
            else
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation line templates. DB table `conversation_line_template` is empty.");
            }

            SQLResult actorResult = DB.World.Query("SELECT ConversationId, ConversationActorId, Idx FROM conversation_actors");
            if (!actorResult.IsEmpty())
            {
                uint oldMSTime = Time.GetMSTime();
                uint count = 0;

                do
                {
                    uint conversationId = actorResult.Read<uint>(0);
                    uint actorId = actorResult.Read<uint>(1);
                    ushort idx = actorResult.Read<ushort>(2);

                    ConversationActorTemplate conversationActorTemplate = _conversationActorTemplateStorage.LookupByKey(actorId);
                    if (conversationActorTemplate != null)
                    {
                        if (!actorsByConversation.ContainsKey(conversationId))
                            actorsByConversation[conversationId] = new ConversationActorTemplate[idx + 1];

                        ConversationActorTemplate[] actors = actorsByConversation[conversationId];
                        if (actors.Length <= idx)
                            Array.Resize(ref actors, idx + 1);

                        actors[idx] = conversationActorTemplate;
                        ++count;
                    }
                    else
                        Log.outError(LogFilter.Sql, "Table `conversation_actors` references an invalid actor (ID: {0}) for Conversation {1}, skipped", actorId, conversationId);
                }
                while (actorResult.NextRow());

                Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Conversation actors in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
            }
            else
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation actors. DB table `conversation_actors` is empty.");
            }

            SQLResult templateResult = DB.World.Query("SELECT Id, FirstLineId, LastLineEndTime, VerifiedBuild FROM conversation_template");
            if (!templateResult.IsEmpty())
            {
                uint oldMSTime = Time.GetMSTime();

                do
                {
                    ConversationTemplate conversationTemplate = new ConversationTemplate();
                    conversationTemplate.Id = templateResult.Read<uint>(0);
                    conversationTemplate.FirstLineId = templateResult.Read<uint>(1);
                    conversationTemplate.LastLineEndTime = templateResult.Read<uint>(2);

                    conversationTemplate.Actors = actorsByConversation[conversationTemplate.Id].ToList();

                    ConversationLineRecord currentConversationLine = CliDB.ConversationLineStorage.LookupByKey(conversationTemplate.FirstLineId);
                    if (currentConversationLine == null)
                        Log.outError(LogFilter.Sql, "Table `conversation_template` references an invalid line (ID: {0}) for Conversation {1}, skipped", conversationTemplate.FirstLineId, conversationTemplate.Id);

                    while (currentConversationLine != null)
                    {
                        ConversationLineTemplate conversationLineTemplate = _conversationLineTemplateStorage.LookupByKey(currentConversationLine.Id);
                        if (conversationLineTemplate != null)
                            conversationTemplate.Lines.Add(conversationLineTemplate);
                        else
                            Log.outError(LogFilter.Sql, "Table `conversation_line_template` has missing template for line (ID: {0}) in Conversation {1}, skipped", currentConversationLine.Id, conversationTemplate.Id);

                        if (currentConversationLine.NextLineID == 0)
                            break;

                        currentConversationLine = CliDB.ConversationLineStorage.LookupByKey(currentConversationLine.NextLineID);
                    }

                    _conversationTemplateStorage[conversationTemplate.Id] = conversationTemplate;
                }
                while (templateResult.NextRow());

                Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Conversation templates in {1} ms", _conversationTemplateStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
            }
            else
            {
                Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation templates. DB table `conversation_template` is empty.");
            }
        }

        public ConversationTemplate GetConversationTemplate(uint conversationId)
        {
            return _conversationTemplateStorage.LookupByKey(conversationId);
        }

        Dictionary<uint, ConversationTemplate> _conversationTemplateStorage = new Dictionary<uint, ConversationTemplate>();
        Dictionary<uint, ConversationActorTemplate> _conversationActorTemplateStorage = new Dictionary<uint, ConversationActorTemplate>();
        Dictionary<uint, ConversationLineTemplate> _conversationLineTemplateStorage = new Dictionary<uint, ConversationLineTemplate>();
    }

    public class ConversationActorTemplate
    {
        public uint Id;
        public uint CreatureId;
        public uint CreatureModelId;
    }

    public class ConversationLineTemplate
    {
        public uint Id;          // Link to ConversationLine.db2
        public uint StartTime;   // Time in ms after conversation creation the line is displayed
        public uint UiCameraID;  // Link to UiCamera.db2
        public ushort ActorIdx;    // Index from conversation_actors
        public ushort Unk;
    }

    public class ConversationTemplate
    {
        public uint Id;
        public uint FirstLineId;     // Link to ConversationLine.db2
        public uint LastLineEndTime; // Time in ms after conversation creation the last line fades out

        public List<ConversationActorTemplate> Actors = new List<ConversationActorTemplate>();
        public List<ConversationLineTemplate> Lines = new List<ConversationLineTemplate>();
    }


}
