/* 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;
using System.IO;

namespace AHD.ID3Tag
{
    /// <summary>
    /// A class can load and save ID3 tag version 1
    /// </summary>
    public class ID3v1
    {
        string filePath = "";
        string title = "";
        string artist = "";
        string album = "";
        string year = "";
        string comment = "";
        byte genreByte = 0;
        byte track = 0;
        Encoding encoding = new ASCIIEncoding();
        static string[] genres =
		{
			"Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal",
			"New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial",
			"Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop",
			"Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House",
			"Game","Sound Clip","Gospel","Noise","Alternative Rock","Bass","Soul","Punk","Space",
			"Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic",
			"Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream",
			"Southern Rock","Comedy","Cult","Gangsta","Top 40","Christian Rap","Pop/Funk","Jungle",
			"Native American","Cabaret","New Wave","Psychadelic","Rave","Showtunes","Trailer","Lo-Fi",
			"Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk",
			"Folk/Rock","National Folk","Swing","Fast-Fusion","Bebob","Latin","Revival","Celtic","Bluegrass",
			"Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock",
			"Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music",
			"Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club",
			"Tango","Samba","Folklore","Ballad","Power Ballad","Rhytmic Soul","Freestyle","Duet",
			"Punk Rock","Drum Solo","Acapella","Euro-House","Dance Hall","Goa","Drum & Bass","Club-House",
			"Hardcore","Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap",
			"Heavy Metal","Black Metal","Crossover","Contemporary Christian",
			"Christian Rock","Merengue","Salsa","Trash Metal","Anime","JPop","SynthPop"
		};
        public Encoding Encoding
        { get { return encoding; } set { encoding = value; } }
        public string FilePath
        { get { return filePath; } set { filePath = value; } }
        public string Title
        { get { return title; } set { title = value; } }
        public string Artist
        { get { return artist; } set { artist = value; } }
        public string Album
        { get { return album; } set { album = value; } }
        public string Year
        { get { return year != "" ? year : "2012"; } set { year = value; } }
        public string Comment
        { get { return comment; } set { comment = value; } }
        public string Genre
        {
            get
            {
                if (genreByte < genres.Length)
                    return genres[genreByte];
                else
                    return "Blues";
            }
            set
            {
                for (byte i = 0; i < genres.Length; i++)
                {
                    if (value == genres[i])
                    {
                        genreByte = i;
                        break;
                    }
                }
            }
        }
        public byte TrackNumber
        { get { return track; } set { track = value; } }
        public static string[] Genres
        {
            get { return genres; }
        }
        /// <summary>
        /// Check if mp3 file has ID3v1
        /// </summary>
        /// <param name="fileName">The full mp3 file path</param>
        /// <returns>True if given file has ID3v1, otherwise false</returns>
        public bool Check(string fileName)
        {
            Stream str = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            str.Position = str.Length - 128;
            byte[] header = new byte[3];
            str.Read(header, 0, 3);
            str.Dispose();
            str.Close();
            ASCIIEncoding enc = new ASCIIEncoding();
            string tag = enc.GetString(header);
            if (enc.GetString(header) == "TAG")
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// Load ID3v1 from mp3 file
        /// </summary>
        /// <param name="fileName">The full mp3 file path</param>
        public void Load(string fileName)
        {
            filePath = fileName;
            if (!Check(fileName))
                throw new NotID3v1Exception();
            Stream str = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            str.Position = str.Length - 125;
            byte[] data = new byte[30];
            //load
            ASCIIEncoding encoding = new ASCIIEncoding();
            //title
            str.Read(data, 0, 30);
            title = encoding.GetString(data);
            //artist
            data = new byte[30];
            str.Read(data, 0, 30);
            artist = encoding.GetString(data);
            //album
            data = new byte[30];
            str.Read(data, 0, 30);
            album = encoding.GetString(data);
            //year
            data = new byte[4];
            str.Read(data, 0, 4);
            year = encoding.GetString(data);
            //comment
            data = new byte[30];
            str.Read(data, 0, 30);
            comment = encoding.GetString(data);
            //Track number ?
            if (data[28] == 0 && data[29] != 0)
                track = data[29];
            //genre
            genreByte = (byte)str.ReadByte();
            //finish
            str.Dispose();
            str.Close();
        }
        /// <summary>
        /// Save ID3v1 to mp3 file located at "FilePath" property path
        /// </summary>
        public void Save()
        { Save(filePath); }
        /// <summary>
        /// Save ID3v1 to mp3 file
        /// </summary>
        /// <param name="fileName">The full mp3 file path</param>
        public void Save(string fileName)
        {
            filePath = fileName;
            //1 clear
            Clear(fileName);
            //2 create dynamic buffer
            List<byte> buffer = new List<byte>();
            //header
            buffer.AddRange(encoding.GetBytes("TAG"));
            //Title
            if (title.Length > 30)
                title = title.Substring(0, 30);
            else
                for (int i = title.Length; i < 30; i++)
                    title += "\0";
            buffer.AddRange(encoding.GetBytes(title));
            //Artist
            if (artist.Length > 30)
                artist = artist.Substring(0, 30);
            else
                for (int i = artist.Length; i < 30; i++)
                    artist += "\0";
            buffer.AddRange(encoding.GetBytes(artist));
            //Album
            if (album.Length > 30)
                album = album.Substring(0, 30);
            else
                for (int i = album.Length; i < 30; i++)
                    album += "\0";
            buffer.AddRange(encoding.GetBytes(album));
            //Year
            if (year.Length != 4)
                year = "2012";
            buffer.AddRange(encoding.GetBytes(year));
            //Comment
            if (comment.Length > 28)
                comment = comment.Substring(0, 28);
            for (int i = comment.Length; i < 29; i++)
                comment += "\0";
            buffer.AddRange(encoding.GetBytes(comment));
            //Track
            buffer.Add(track);
            //Genre
            buffer.Add(genreByte);

            //File streams and other things...
            //create a read stream
            Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            int j = 0;
            string tempFolder = Path.GetDirectoryName(filePath);
            if (tempFolder.Length == 0)
                tempFolder = Path.GetPathRoot(filePath);

            string tempName = tempFolder + Path.GetFileNameWithoutExtension(filePath) + "_temp" + j;
            while (File.Exists(tempName))
            {
                j++;
                tempName = tempFolder + Path.GetFileNameWithoutExtension(filePath) + "_temp" + j;
            }
            Stream tempStream = new FileStream(tempName, FileMode.Create, FileAccess.Write);

            //get buffer, the mp3 data and ID3v2 if found (we cleared ID3v1 before)
            byte[] tempBuffer = new byte[stream.Length];
            stream.Position = 0;
            stream.Read(tempBuffer, 0, tempBuffer.Length);
            stream.Dispose();
            stream.Close();

            //write the buffer to temp
            tempStream.Write(tempBuffer, 0, tempBuffer.Length);
            tempStream.Write(buffer.ToArray(), 0, buffer.Count);
            tempStream.Dispose();
            tempStream.Close();

            //delete original, rename temp then delete it
            File.Delete(filePath);
            File.Copy(tempName, filePath);
            File.Delete(tempName);
            //done !!
        }

        /// <summary>
        /// Clear ID3v1 from currenlty opened file
        /// </summary>
        public void Clear()
        {
            Clear(this.filePath);
        }
        /// <summary>
        /// Clear ID3v1 from a file
        /// </summary>
        /// <param name="filePath">The complete mp3 file path</param>
        public void Clear(string filePath)
        {
            this.filePath = filePath;
            //check file
            if (!Check(filePath))
                return;
            //1 create a read stream
            Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            //2 create temp file
            int i = 0;
            string tempFolder = Path.GetDirectoryName(filePath);
            if (tempFolder.Length == 0)
                tempFolder = Path.GetPathRoot(filePath);

            string tempName = tempFolder + Path.GetFileNameWithoutExtension(filePath) + "_temp" + i;
            while (File.Exists(tempName))
            {
                i++;
                tempName = tempFolder + Path.GetFileNameWithoutExtension(filePath) + "_temp" + i;
            }
            Stream tempStream = new FileStream(tempName, FileMode.Create, FileAccess.Write);
            //5 copy the same file but without ID3 tag
            //get buffer
            byte[] tempBuffer = new byte[stream.Length - 128];
            stream.Position = 0;
            stream.Read(tempBuffer, 0, tempBuffer.Length);
            stream.Dispose();
            stream.Close();

            //write the buffer to temp
            tempStream.Write(tempBuffer, 0, tempBuffer.Length);
            tempStream.Dispose();
            tempStream.Close();

            //6 delete original, rename temp then delete it
            File.Delete(filePath);
            File.Copy(tempName, filePath);
            File.Delete(tempName);
        }
        /// <summary>
        /// Clear the fields but not from file
        /// </summary>
        public void ClearFileds()
        {
            title = artist = album = comment = "";
            year = "2012";
            genreByte = track = 0;
        }
    }
}
