using System;
using System.Collections.Generic;
using System.IO;
using Oni.Metadata;

namespace Oni
{
    internal sealed class InstanceDescriptor
    {
        private InstanceFile file;
        private string fullName;
        private int index;
        private Template template;
        private int dataOffset;
        private int nameOffset;
        private int dataSize;
        private InstanceDescriptorFlags flags;

        internal static InstanceDescriptor Read(InstanceFile file, BinaryReader reader, int index)
        {
            var metadata = InstanceMetadata.GetMetadata(file);

            var descriptor = new InstanceDescriptor
            {
                file = file,
                index = index,
                template = metadata.GetTemplate((TemplateTag)reader.ReadInt32()),
                dataOffset = reader.ReadInt32(),
                nameOffset = reader.ReadInt32(),
                dataSize = reader.ReadInt32(),
                flags = (InstanceDescriptorFlags)(reader.ReadInt32() & 0xff)
            };

            if (descriptor.IsPlaceholder && !descriptor.HasName)
                throw new InvalidDataException("Empty descriptors must have names");

            return descriptor;
        }

        public InstanceFile File => file;

        public int Index => index;

        public string FullName
        {
            get
            {
                if (fullName == null)
                    fullName = index.ToString();

                return fullName;
            }
        }

        public string Name
        {
            get
            {
                string name = FullName;

                if (name.StartsWith(Template.Tag.ToString(), StringComparison.Ordinal))
                    name = name.Substring(4);

                return name;
            }
        }

        public Template Template => template;

        public bool HasName => ((flags & InstanceDescriptorFlags.Private) == 0);

        public bool IsPlaceholder => ((flags & InstanceDescriptorFlags.Placeholder) != 0 || dataSize == 0 || dataOffset == 0);

        public int DataOffset => file.Header.DataTableOffset + dataOffset;

        public int DataSize => dataSize;

        internal void ReadName(Dictionary<int, string> names)
        {
            if (!HasName)
                return;

            if (IsPlaceholder || file.Header.Version == InstanceFileHeader.Version31)
            {
                names.TryGetValue(nameOffset, out fullName);
            }
            else
            {
                fullName = Importer.DecodeFileName(file.FilePath);

                string tagName = template.Tag.ToString();

                if (!fullName.StartsWith(tagName, StringComparison.Ordinal))
                    fullName = tagName + fullName;
            }
        }

        internal void SetName(string newName)
        {
            flags &= ~InstanceDescriptorFlags.Private;
            fullName = newName;
        }

        public List<InstanceDescriptor> GetReferencedDescriptors() => file.GetReferencedDescriptors(this);

        internal BinaryReader OpenRead()
        {
            if (IsPlaceholder)
                throw new InvalidOperationException();

            return new BinaryReader(file.FilePath, file)
            {
                Position = DataOffset
            };
        }

        internal BinaryReader OpenRead(int offset)
        {
            if (IsPlaceholder)
                throw new InvalidOperationException();

            return new BinaryReader(file.FilePath, file)
            {
                Position = DataOffset + offset
            };
        }

        internal BinaryReader GetRawReader(int offset) => file.GetRawReader(offset);

        internal BinaryReader GetSepReader(int offset)
        {
            if (!IsMacFile)
                return GetRawReader(offset);

            return file.GetSepReader(offset);
        }

        internal bool IsMacFile => (file.Header.TemplateChecksum == InstanceFileHeader.OniMacTemplateChecksum);

        public long TemplateChecksum => file.Header.TemplateChecksum;

        public string FilePath => file.FilePath;

        public bool HasRawParts()
        {
            if (IsPlaceholder)
                return false;

            switch (template.Tag)
            {
                case TemplateTag.AKVA:
                case TemplateTag.AGDB:
                case TemplateTag.BINA:
                case TemplateTag.TXMP:
                case TemplateTag.OSBD:
                case TemplateTag.SNDD:
                case TemplateTag.SUBT:
                case TemplateTag.TRAM:
                    return true;

                default:
                    return false;
            }
        }
    }
}
