/*
 * Character.cs
 * Copyright (c) 2007-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync 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.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace LipSync {

    public enum CharacterType {
        def,
        plugin
    }

    [Serializable]
    public class Character : IDisposable {
        public CharacterType type;
        string m_Name;
        public PluginConfig m_pluginConfig;
        public Size m_plugin_drawarea;

        [Obsolete]
        public Image Base;
        [Obsolete]
        public Image a;
        [Obsolete]
        public Image aa;
        [Obsolete]
        public Image i;
        [Obsolete]
        public Image u;
        [Obsolete]
        public Image e;
        [Obsolete]
        public Image o;
        [Obsolete]
        public Image xo;
        [Obsolete]
        public Image nn;

        public List<ImageEntry> Images;
        [NonSerialized]
        private Bitmap m_cache = null;
        [NonSerialized]
        private string m_cache_draw = "";
        [OptionalField]
        public string version;//setDefaultで変更するので、readonlyにしたいけどできん
        [OptionalField]
        public Size m_size;
        [OptionalField]
        public Bitmap m_preview = null;

        [NonSerialized]
        bool internal_operation = false;

        [OnDeserializing]
        private void onDeserializing( StreamingContext sc ) {
            version = "0";
            m_size = new Size( 0, 0 );
        }

        [OnSerialized]
        private void onSerialized( StreamingContext sc ) {
            if ( m_size.Equals( new Size( 0, 0 ) ) ) {
                foreach ( ImageEntry img in Images ) {
                    if ( img.title == "base" ) {
                        if ( img.Image != null ) {
                            m_size = new Size( img.Image.Width, img.Image.Height );
                            break;
                        }
                    }
                }
            }
        }

        [OnDeserialized]
        private void onDeserialized( StreamingContext sc ){
            // 古いバージョンの*.lseとの互換性を保つため。
            // base, a, ..., nnのデータをImagesに移動させる
            if ( nn != null ) {
                Images.Insert( 0, new ImageEntry( "nn", nn, "口", false ) );
                nn.Dispose();
            }
            if ( xo != null ) {
                Images.Insert( 0, new ImageEntry( "xo", xo, "口", false ) );
                xo.Dispose();
            }
            if ( o != null ) {
                Images.Insert( 0, new ImageEntry( "o", o, "口", false ) );
                o.Dispose();
            }
            if ( e != null ) {
                Images.Insert( 0, new ImageEntry( "e", e, "口", false ) );
                e.Dispose();
            }
            if ( u != null ) {
                Images.Insert( 0, new ImageEntry( "u", u, "口", false ) );
                u.Dispose();
            }
            if ( i != null ) {
                Images.Insert( 0, new ImageEntry( "i", i, "口", false ) );
                i.Dispose();
            }
            if ( aa != null ) {
                Images.Insert( 0, new ImageEntry( "aa", aa, "口", false ) );
                aa.Dispose();
            }
            if ( a != null ) {
                Images.Insert( 0, new ImageEntry( "a", a, "口", false ) );
                a.Dispose();
            }
            if ( Base != null ) {
                Images.Insert( 0, new ImageEntry( "base", Base, "本体", true ) );
                Base.Dispose();
            }
            if ( version == "0" ) {
                for ( int k = 0; k < Images.Count; k++ ) {
                    if ( Images[k].title == "base" ) {
                        Images[k].IsDefault = true;
                        m_size = Images[k].Image.Size;
                        version = "2";
                    }
                }
            }

            if ( m_size.Width <= 0 || m_size.Height <= 0 ) {
                int max_x = 0;
                int max_y = 0;
                foreach ( ImageEntry img in Images ) {
                    if ( img.Image != null ) {
                        max_x = Math.Max( max_x, img.Image.Width + img.XOffset );
                        max_y = Math.Max( max_y, img.Image.Height + img.YOffset );
                    }
                }
                m_size = new Size( max_x, max_y );
            }

        }

        public void Dispose() {
            m_Name = null;
            m_pluginConfig = null;
            if ( Base != null ) {
                Base.Dispose();
            }
            if ( a != null ) {
                a.Dispose();
            }
            if ( aa != null ) {
                aa.Dispose();
            }
            if ( i != null ) {
                i.Dispose();
            }
            if ( u != null ) {
                u.Dispose();
            }
            if ( e != null ) {
                e.Dispose();
            }
            if ( o != null ) {
                o.Dispose();
            }
            if ( xo != null ) {
                xo.Dispose();
            }
            if ( nn != null ) {
                nn.Dispose();
            }
            Images.Clear();
        }

        public void Write( Stream stream ) {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize( stream, this );
        }

        public string GetMD5() {
            System.Security.Cryptography.MD5CryptoServiceProvider mcsp = new System.Security.Cryptography.MD5CryptoServiceProvider();
            using ( MemoryStream ms = new MemoryStream() ) {
                this.Write( ms );
                byte[] dat = mcsp.ComputeHash( ms );
                string res = "";
                foreach ( byte b in dat ) {
                    res += b.ToString( "x2" );
                }
                return res;
            }
            return "";
        }

        public static Character FromFile( Stream stream ) {
            Character result = null;
            BinaryFormatter bf = new BinaryFormatter();
            result = (Character)bf.Deserialize( stream );
            bf = null;
            return result;
        }

        public static Character FromFile( string filepath ) {
            Character result = null;
            if ( File.Exists( filepath ) ) {
                using ( FileStream fs = new FileStream( filepath, FileMode.Open ) ) {
                    result = FromFile( fs );
                }
            }
            return result;
        }

        public int Width {
            get {
                return m_size.Width;
            }
        }

        public int Height {
            get {
                return m_size.Height;
            }
        }

        public string Name {
            get {
                return m_Name;
            }
            set {
                m_Name = value;
            }
        }

        public Bitmap DefaultFace {
            get {
                string type = "";
                foreach ( ImageEntry img in this.Images ) {
                    if ( img.IsDefault ) {
                        type += img.title + "\n";
                    }
                }
                Bitmap res = Face( type );
#if DEBUG
                if ( res == null ) {
                    Common.DebugWriteLine( "Character.DefaultFace.get(); res==null" );
                    Common.DebugWriteLine( "                             Width=" + Width );
                    Common.DebugWriteLine( "                             Height=" + Height );
                    Common.DebugWriteLine( "                             type=" + type );
                }
#endif
                return res;
            }
        }

        public Bitmap Face( string type ) {
            if ( Width <= 0 || Height <= 0 ) {
                return null;
            }

            if ( m_cache != null ) {
                if ( m_cache_draw == type ) {
                    return (Bitmap)m_cache.Clone();
                }
            }

            Bitmap bmp = new Bitmap( Width, Height );
            string[] spl = type.Split( "\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
            using ( Graphics g = Graphics.FromImage( bmp ) ) {
                //Image drawing;
                for ( int i = 0; i < spl.Length; i++ ) {
                    for ( int index = 0; index < Images.Count; index++ ) {
                        if ( Images[index].title == spl[i] ) {
                            //drawing = Images[index].image;
                            Images[index].DrawTo( g );
                            break;
                        }
                    }
                }
            }

            m_cache_draw = type;
            if ( m_cache != null ) {
                m_cache.Dispose();
            }
            m_cache = (Bitmap)bmp.Clone();
            return bmp;
        }
    }

}
