/* This file is part of AHD ID3 Tag Editor (AITE)
   A program can create and edit ID3 Tag for mp3 files

   Copyright © Ala I.Hadid 2012

   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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AHD.ID3Tag
{
    /// <summary>
    /// Class represents frame
    /// </summary>
    public abstract class ID3v2Frame
    {
        /// <summary>
        /// Create new instance of ID3v2 frame class
        /// </summary>
        /// <param name="id">The ID of this frame</param>
        /// <param name="size">The size of this frame</param>
        /// <param name="flags">The flags, should be 2 bytes</param>
        /// <param name="data">The data of this frame</param>
        public ID3v2Frame(string id, int size, ushort flags, byte[] data)
        {
            this.id = id;
            this.flags = flags;
            this.data = data;
            this.size = size;
            ApplyFlags();
        }

        string id = "";
        ushort flags = 0;
        byte[] data;
        int size;

        //flags
        bool f_TagAlterPreservation = false;
        bool f_FileAlterPreservation = false;
        bool f_ReadOnly = false;
        bool f_Compression = false;
        bool f_Encryption = false;
        bool f_Unsynchronisation = false;

        /// <summary>
        /// Get or set the id of this flag
        /// </summary>
        public virtual string ID
        { get { return id; } set { id = value; } }
        /// <summary>
        /// Get the name of this frame
        /// </summary>
        public abstract string Name { get; }
        /// <summary>
        /// Get or set the size of this frame (includeing the header)
        /// </summary>
        public virtual int Size
        { get { return GetSize() + 10; } }
        /// <summary>
        /// Get the size as read from the header
        /// </summary>
        public int ReadSize
        { get { return size; } set { size = value; } }

        /// <summary>
        /// Get or set the data of this frame
        /// </summary>
        public virtual byte[] Data
        { get { return data; } set { data = value; } }
        /// <summary>
        /// Get or set the falgs of this frame
        /// </summary>
        public virtual ushort Flags
        { get { return flags = GetFlags(); } set { flags = value; ApplyFlags(); } }

        public bool Flag_TagAlterPreservation
        { get { return f_TagAlterPreservation; } set { f_TagAlterPreservation = value; } }
        public bool Flag_FileAlterPreservation
        { get { return f_FileAlterPreservation; } set { f_FileAlterPreservation = value; } }
        public bool Flag_ReadOnly
        { get { return f_ReadOnly; } set { f_ReadOnly = value; } }
        public bool Flag_Compression
        { get { return f_Compression; } set { f_Compression = value; } }
        public bool Flag_Encryption
        { get { return f_Encryption; } set { f_Encryption = value; } }
        public bool Flag_Unsynchronisation
        { get { return f_Unsynchronisation; } set { f_Unsynchronisation = value; } }

        /// <summary>
        /// Calculate the size occurding to current values and data
        /// </summary>
        /// <returns>The frame size EXCLUDING the header</returns>
        public abstract int GetSize();

        void ApplyFlags()
        {
            f_TagAlterPreservation = (flags & 0x8000) == 0x8000;//bit7 byteH
            f_FileAlterPreservation = (flags & 0x4000) == 0x4000;//bit6 byteH
            f_ReadOnly = (flags & 0x2000) == 0x2000;//bit5 byteH

            f_Compression = (flags & 0x0080) == 0x0080;//bit7 byteL
            f_Encryption = (flags & 0x0040) == 0x0040;//bit6 byteL
            f_Unsynchronisation = (flags & 0x0020) == 0x0020;//bit5 byteL
        }
        ushort GetFlags()
        {
            ushort flgs = 0;
            if (f_TagAlterPreservation)
                flags |= 0x8000;
            if (f_FileAlterPreservation)
                flags |= 0x4000;
            if (f_ReadOnly)
                flags |= 0x2000;

            if (f_Compression)
                flags |= 0x0080;
            if (f_Encryption)
                flags |= 0x0040;
            if (f_Unsynchronisation)
                flags |= 0x0020;
            return flgs;
        }
        /// <summary>
        /// Load this frame ("Data" property must be presented)
        /// </summary>
        public abstract void Load();
        /// <summary>
        /// Save this frame data into "Data" property WITHOUT header
        /// </summary>
        public abstract void Save();
        /// <summary>
        /// Get the header bytes, should be 10 length
        /// </summary>
        /// <param name="tagVersion">The tag minor virsion (2.x)</param>
        /// <returns>header bytes, should be 10 length</returns>
        public virtual byte[] GetHeader(byte tagVersion)
        {
            List<byte> header = new List<byte>();

            //id, 4 bytes
            header.AddRange(ID3v2EncodingWrapper.ISO_8859_1.GetBytes(this.id));
            //size, 4 bytes
            if (tagVersion == 3)
                header.AddRange(SynchsafeConvertor.ToInt32Bytes(GetSize()));
            if (tagVersion == 4)
                header.AddRange(SynchsafeConvertor.ToSynchsafeBytes(GetSize()));

            //flags, 2 bytes
            ushort flgs = GetFlags();
            header.Add((byte)((flgs & 0xFF00) >> 08));
            header.Add((byte)((flgs & 0x00FF) >> 00));
            //finish
            return header.ToArray();
        }
        /// <summary>
        /// ID3v2Fraem.ToString()
        /// </summary>
        /// <returns>The name of this frame</returns>
        public override string ToString()
        {
            return this.Name;
        }
        /// <summary>
        /// Get the compatible tag version for this frame, this value is the minor version (2.x while x=this property)
        /// </summary>
        public virtual byte[] CompatibleTagVersions { get { return new byte[] { 3, 4 }; } }
        /// <summary>
        /// Get a value indecate wether a tag version is compatible with this frame
        /// </summary>
        /// <param name="tagVersion">The minor tag version (2.x while x=this parameter)</param>
        /// <returns>True if this frame is compatible with given version, otherwise false</returns>
        public virtual bool IsCompatibleVersion(byte tagVersion)
        {
            foreach (byte version in CompatibleTagVersions)
                if (version == tagVersion)
                    return true;
            return false;
        }
    }
}
