﻿using System;
using System.Collections.Generic;
using Oni.Metadata;

namespace Oni.Objects
{
    internal class ObjcDatWriter : Importer
    {
        private readonly TypeTag tag;
        private readonly string name;
        private readonly List<Objects.ObjectBase> objects;
        private readonly Type type;

        private ObjcDatWriter(TypeTag tag, string name, List<Objects.ObjectBase> objects)
        {
            this.tag = tag;
            this.name = name;
            this.objects = objects;

            if (tag == TypeTag.CHAR)
                type = typeof(Character);
            else if (tag == TypeTag.WEAP)
                type = typeof(Weapon);
            else if (tag == TypeTag.PART)
                type = typeof(Particle);
            else if (tag == TypeTag.PWRU)
                type = typeof(PowerUp);
            else if (tag == TypeTag.FLAG)
                type = typeof(Flag);
            else if (tag == TypeTag.DOOR)
                type = typeof(Door);
            else if (tag == TypeTag.CONS)
                type = typeof(Objects.Console);
            else if (tag == TypeTag.FURN)
                type = typeof(Furniture);
            else if (tag == TypeTag.TRIG)
                type = typeof(Trigger);
            else if (tag == TypeTag.TRGV)
                type = typeof(TriggerVolume);
            else if (tag == TypeTag.SNDG)
                type = typeof(Objects.Sound);
            else if (tag == TypeTag.TURR)
                type = typeof(Turret);
            else if (tag == TypeTag.NEUT)
                type = typeof(Neutral);
            else if (tag == TypeTag.PATR)
                type = typeof(PatrolPath);
        }

        private enum TypeTag
        {
            CHAR = 0x43484152,
            CMBT = 0x434d4254,
            CONS = 0x434f4e53,
            DOOR = 0x444f4f52,
            FLAG = 0x464c4147,
            FURN = 0x4655524e,
            MELE = 0x4d454c45,
            NEUT = 0x4e455554,
            PART = 0x50415254,
            PATR = 0x50415452,
            PWRU = 0x50575255,
            SNDG = 0x534e4447,
            TRGV = 0x54524756,
            TRIG = 0x54524947,
            TURR = 0x54555252,
            WEAP = 0x57454150
        }

        public static void Write(List<Objects.ObjectBase> objects, string outputDirPath)
        {
            System.Console.Error.WriteLine("Writing {0} objects...", objects.Count);

            Write(TypeTag.CHAR, "Character", objects, outputDirPath);
            Write(TypeTag.CONS, "Console", objects, outputDirPath);
            Write(TypeTag.DOOR, "Door", objects, outputDirPath);
            Write(TypeTag.FLAG, "Flag", objects, outputDirPath);
            Write(TypeTag.FURN, "Furniture", objects, outputDirPath);
            Write(TypeTag.NEUT, "Neutral", objects, outputDirPath);
            Write(TypeTag.PART, "Particle", objects, outputDirPath);
            Write(TypeTag.PATR, "Patrol Path", objects, outputDirPath);
            Write(TypeTag.PWRU, "PowerUp", objects, outputDirPath);
            Write(TypeTag.SNDG, "Sound", objects, outputDirPath);
            Write(TypeTag.TRIG, "Trigger", objects, outputDirPath);
            Write(TypeTag.TRGV, "Trigger Volume", objects, outputDirPath);
            Write(TypeTag.TURR, "Turret", objects, outputDirPath);
            Write(TypeTag.WEAP, "Weapon", objects, outputDirPath);
        }

        private static void Write(TypeTag tag, string name, List<Objects.ObjectBase> objects, string outputDirPath)
        {
            var writer = new ObjcDatWriter(tag, name, objects);
            writer.Import(null, outputDirPath);
        }

        public override void Import(string filePath, string outputDirPath)
        {
            BeginImport();

            var bina = CreateInstance(TemplateTag.BINA, "CJBO" + name);

            int offset = RawWriter.Align32();
            int size = WriteCollection(RawWriter);

            using (var writer = bina.OpenWrite())
            {
                writer.Write(size);
                writer.Write(offset);
            }

            Write(outputDirPath);
        }

        private int WriteCollection(BinaryWriter raw)
        {
            int start = raw.Position;

            raw.Write((int)BinaryTag.OBJC);
            raw.Write(0);
            raw.Write(39);

            foreach (var obj in objects.Where(o => o.GetType() == type))
            {
                int objectStartPosition = raw.Position;
                raw.Write(0);
                raw.Write((int)tag);

                obj.Write(raw);

                raw.Position = Utils.Align4(raw.Position);
                int objectSize = raw.Position - objectStartPosition - 4;
                raw.WriteAt(objectStartPosition, objectSize);
            }

            raw.Write(0);
            int size = raw.Position - start;
            raw.WriteAt(start + 4, size - 8);
            return size;
        }
    }
}
