/*
 * SingerConfig.cs
 * Copyright (c) 2008-2009 kbinani
 *
 * This file is part of Boare.Lib.Vsq.
 *
 * Boare.Lib.Vsq is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * Boare.Lib.Vsq 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.
 */
using System;
using System.IO;
using System.Collections.Generic;

namespace Boare.Lib.Vsq {

    public class SingerConfig : ICloneable {
        public string ID = "";
        public string FORMAT = "";
        public string  VOICEIDSTR = "";
        public string VOICENAME = "Unknown";
        public int Breathiness;
        public int Brightness;
        public int Clearness;
        public int Opening;
        public int GenderFactor;
        public int Original;
        public int Program;

        public SingerConfig() {
        }
        
        public object Clone() {
            SingerConfig ret = new SingerConfig();
            ret.ID = ID;
            ret.FORMAT = FORMAT;
            ret.VOICEIDSTR = VOICEIDSTR;
            ret.VOICENAME = VOICENAME;
            ret.Breathiness = Breathiness;
            ret.Brightness = Brightness;
            ret.Clearness = Clearness;
            ret.Opening = Opening;
            ret.GenderFactor = GenderFactor;
            ret.Original = Original;
            ret.Program = Program;
            return ret;
        }

        public static void decode_vvd_bytes( ref byte[] dat ) {
            for ( int i = 0; i < dat.Length; i++ ) {
                byte M = (byte)(dat[i] >> 4);
                byte L = (byte)(dat[i] - (M << 4));
                byte newM = endecode_vvd_m( M );
                byte newL = endecode_vvd_l( L );
                dat[i] = (byte)((newM << 4) | newL);
            }
        }

        static byte endecode_vvd_l( byte value ) {
            switch ( value ) {
                case 0x0:
                    return 0xa;
                case 0x1:
                    return 0xb;
                case 0x2:
                    return 0x8;
                case 0x3:
                    return 0x9;
                case 0x4:
                    return 0xe;
                case 0x5:
                    return 0xf;
                case 0x6:
                    return 0xc;
                case 0x7:
                    return 0xd;
                case 0x8:
                    return 0x2;
                case 0x9:
                    return 0x3;
                case 0xa:
                    return 0x0;
                case 0xb:
                    return 0x1;
                case 0xc:
                    return 0x6;
                case 0xd:
                    return 0x7;
                case 0xe:
                    return 0x4;
                case 0xf:
                    return 0x5;
            }
            return 0x0;
        }

        static byte endecode_vvd_m( byte value ) {
            switch ( value ) {
                case 0x0:
                    return 0x1;
                case 0x1:
                    return 0x0;
                case 0x2:
                    return 0x3;
                case 0x3:
                    return 0x2;
                case 0x4:
                    return 0x5;
                case 0x5:
                    return 0x4;
                case 0x6:
                    return 0x7;
                case 0x7:
                    return 0x6;
                case 0x8:
                    return 0x9;
                case 0x9:
                    return 0x8;
                case 0xa:
                    return 0xb;
                case 0xb:
                    return 0xa;
                case 0xc:
                    return 0xd;
                case 0xd:
                    return 0xc;
                case 0xe:
                    return 0xf;
                case 0xf:
                    return 0xe;
            }
            return 0x0;
        }

        public static SingerConfig readSingerConfig( string file, int original ) {
            SingerConfig sc = new SingerConfig();
            //original = original;
            sc.ID = "VOCALOID:VIRTUAL:VOICE";
            sc.FORMAT = "2.0.0.0";
            sc.VOICEIDSTR = "";
            sc.VOICENAME = "Miku";
            sc.Breathiness = 0;
            sc.Brightness = 0;
            sc.Clearness = 0;
            sc.Opening = 0;
            sc.GenderFactor = 0;
            sc.Original = original; //original = 0;
            FileStream fs = null;
            try {
                fs = new FileStream( file, FileMode.Open, FileAccess.Read );
                int length = (int)fs.Length;
                byte[] dat = new byte[length];
                fs.Read( dat, 0, length );
                decode_vvd_bytes( ref dat );
                for ( int i = 0; i < dat.Length - 1; i++ ) {
                    if ( dat[i] == 0x17 && dat[i + 1] == 0x10 ) {
                        dat[i] = 0x0d;
                        dat[i + 1] = 0x0a;
                    }
                }
                string str = bocoree.cp932.convert( dat );
                string crlf = ((char)0x0d).ToString() + ((char)0x0a).ToString();
                string[] spl = str.Split( new string[] { crlf }, StringSplitOptions.RemoveEmptyEntries );

                foreach ( string s in spl ) {
                    int first = s.IndexOf( '"' );
                    int first_end = get_quated_string( s, first );
                    int second = s.IndexOf( '"', first_end + 1 );
                    int second_end = get_quated_string( s, second );
                    char[] chs = s.ToCharArray();
                    string id = new string( chs, first, first_end - first + 1 );
                    string value = new string( chs, second, second_end - second + 1 );
                    id = id.Substring( 1, id.Length - 2 );
                    value = value.Substring( 1, value.Length - 2 );
                    value = value.Replace( "\\\"", "\"" );
                    if ( id == "ID" ) {
                        sc.ID = value;
                    } else if ( id == "FORMAT" ) {
                        sc.FORMAT = value;
                    } else if ( id == "VOICEIDSTR" ) {
                        sc.VOICEIDSTR = value;
                    } else if ( id == "VOICENAME" ) {
                        sc.VOICENAME = value;
                    } else if ( id == "Breathiness" ) {
                        try {
                            sc.Breathiness = int.Parse( value );
                        } catch {
                        }
                    } else if ( id == "Brightness" ) {
                        try {
                            sc.Brightness = int.Parse( value );
                        } catch {
                        }
                    } else if ( id == "Clearness" ) {
                        try {
                            sc.Clearness = int.Parse( value );
                        } catch {
                        }
                    } else if ( id == "Opening" ) {
                        try {
                            sc.Opening = int.Parse( value );
                        } catch {
                        }
                    } else if ( id == "Gender:Factor" ) {
                        try {
                            sc.GenderFactor = int.Parse( value );
                        } catch {
                        }
                    }
                }
            } catch {

            } finally {
                if ( fs != null ) {
                    fs.Close();
                }
            }
            return sc;
        }

        /// <summary>
        /// 位置positionにある'"'から，次に現れる'"'の位置を調べる．エスケープされた\"はスキップされる．'"'が見つからなかった場合-1を返す
        /// </summary>
        /// <param name="s"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        static int get_quated_string( string s, int position ) {
            if ( position < 0 ) {
                return -1;
            }
            char[] chs = s.ToCharArray();
            if ( position >= chs.Length ) {
                return -1;
            }
            if ( chs[position] != '"' ) {
                return -1;
            }
            int end = -1;
            for ( int i = position + 1; i < chs.Length; i++ ) {
                if ( chs[i] == '"' && chs[i - 1] != '\\' ) {
                    end = i;
                    break;
                }
            }
            return end;
        }

        public string[] ToStringArray() {
            List<string> ret = new List<string>();
            ret.Add( "\"ID\":=:\"" + ID + "\"" );
            ret.Add( "\"FORMAT\":=:\"" + FORMAT + "\"" );
            ret.Add( "\"VOICEIDSTR\":=:\"" + VOICEIDSTR + "\"" );
            ret.Add( "\"VOICENAME\":=:\"" + VOICENAME.Replace( "\"", "\\\"" ) + "\"" );
            ret.Add( "\"Breathiness\":=:\"" + Breathiness + "\"" );
            ret.Add( "\"Brightness\":=:\"" + Brightness + "\"" );
            ret.Add( "\"Clearness\":=:\"" + Clearness + "\"" );
            ret.Add( "\"Opening\":=:\"" + Opening + "\"" );
            ret.Add( "\"Gender:Factor\":=:\"" + GenderFactor + "\"" );
            return ret.ToArray();
        }

        public override string ToString() {
            string[] r = ToStringArray();
            string ret = "";
            foreach ( string s in r ) {
                ret += s + "\n";
            }
            return ret;
        }
    }

}
