﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NLayer.Decoder
{
    class ID3Frame : FrameBase
    {
        internal static ID3Frame TrySync(uint syncMark)
        {
            if ((syncMark & 0xFFFFFF00U) == 0x49443300)
            {
                return new ID3Frame { _version = 2 };
            }

            if ((syncMark & 0xFFFFFF00U) == 0x54414700)
            {
                if ((syncMark & 0xFF) == 0x2B)
                {
                    return new ID3Frame { _version = 1 };
                }
                else
                {
                    return new ID3Frame { _version = 0 };
                }
            }

            return null;
        }

        int _version;

        ID3Frame()
        {

        }

        protected override int Validate()
        {
            switch (_version)
            {
                case 2:
                    // v2, yay!
                    var buf = new byte[7];
                    if (Read(3, buf) == 7)
                    {
                        byte flagsMask;
                        switch (buf[0])
                        {
                            case 3:
                                flagsMask = 0x1F;
                                break;
                            case 4:
                                flagsMask = 0x0F;
                                break;
                            default:
                                return -1;
                        }

                        // ignore the flags (we don't need them for the validation)

                        // get the size (7 bits per byte [MSB cleared])
                        var size = (buf[3] << 21)
                                 | (buf[4] << 14)
                                 | (buf[5] << 7)
                                 | (buf[6]);

                        // finally, check to make sure that all the right bits are cleared
                        if (!(((buf[2] & flagsMask) | (buf[3] & 0x80) | (buf[4] & 0x80) | (buf[5] & 0x80) | (buf[6] & 0x80)) != 0 || buf[1] == 0xFF))
                        {
                            return size + 10;   // don't forget the sync, flag & size bytes!
                        }
                    }
                    break;
                case 1:
                    return 227 + 128;
                case 0:
                    return 128;
            }

            return -1;
        }

        internal override void Parse()
        {
            // assume we have to process it now or else...  we can still read the whole frame, so no biggie
            switch (_version)
            {
                case 2:
                    ParseV2();
                    break;
                case 1:
                    ParseV1Enh();
                    break;
                case 0:
                    ParseV1(3);
                    break;
            }
        }

        void ParseV1(int offset)
        {
            //var buffer = new byte[125];
            //if (Read(offset, buffer) == 125)
            //{
            //    // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
            //    var encoding = Encoding.ASCII;
            //
            //    // title (30)
            //    Title = encoding.GetString(buffer, 0, 30);
            //
            //    // artist (30)
            //    Artist = encoding.GetString(buffer, 30, 30);
            //
            //    // album (30)
            //    Album = encoding.GetString(buffer, 60, 30);
            //
            //    // year (4)
            //    Year = encoding.GetString(buffer, 90, 30);
            //
            //    // comment (30)*
            //    Comment = encoding.GetString(buffer, 94, 30);
            //
            //    if (buffer[122] == 0)
            //    {
            //        // track (1)*
            //        Track = (int)buffer[123];
            //    }
            //
            //    // genre (1)
            //    // ignore for now
            //
            //    // * if byte 29 of comment is 0, track is byte 30.  Otherwise, track is unknown.
            //}
        }

        void ParseV1Enh()
        {
            ParseV1(230);

            //var buffer = new byte[223];
            //if (Read(4, buffer) == 223)
            //{
            //    // v1 tags use ASCII encoding... For now we'll use the built-in encoding, but for Win8 we'll have to build our own.
            //    var encoding = Encoding.ASCII;
            //
            //    // title (60)
            //    Title += encoding.GetString(buffer, 0, 60);
            //
            //    // artist (60)
            //    Artist += encoding.GetString(buffer, 60, 60);
            //
            //    // album (60)
            //    Album += encoding.GetString(buffer, 120, 60);
            //
            //    // speed (1)
            //    //var speed = buffer[180];
            //
            //    // genre (30)
            //    Genre = encoding.GetString(buffer, 181, 30);
            //
            //    // start-time (6)
            //    // 211
            //
            //    // end-time (6)
            //    // 217
            //}
        }

        void ParseV2()
        {
            // v2 is much more complicated than v1...  don't worry about it for now
            // look for any merged frames, as well
        }

        internal int Version
        {
            get
            {
                if (_version == 0) return 1;
                return _version;
            }
        }

        //internal string Title { get; private set; }
        //internal string Artist { get; private set; }
        //internal string Album { get; private set; }
        //internal string Year { get; private set; }
        //internal string Comment { get; private set; }
        //internal int Track { get; private set; }
        //internal string Genre { get; private set; }
        // speed
        //public TimeSpan StartTime { get; private set; }
        //public TimeSpan EndTime { get; private set; }

        internal void Merge(ID3Frame newFrame)
        {
            // just save off the frame for parsing later
        }
    }
}
