/*
 * TimeTable.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.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;

using CurveEditor;

namespace LipSync {

    [Serializable]
    public class TimeTable : IDisposable, ICloneable, IDrawObject {
        private List<TimeTableEntry> list;
        private string stringValue;
        private int type;
        [Obsolete]
        private Image image;
        private Point position;
        private int intValue;
        private int m_zOrder;
        private float scale;
        [OptionalField]
        public BezierChain mc_x;
        [OptionalField]
        public BezierChain mc_y;
        [OptionalField]
        public BezierChain mc_scale;
        [OptionalField]
        public BezierChain mc_alpha;
        [OptionalField]
        public BezierChain mc_rotate;
        /// <summary>
        /// aviをはっつけるモード
        /// </summary>
        [OptionalField]
        private bool m_avi_mode = false;
        [OptionalField]
        private string m_avi_config = "";
        [NonSerialized]
        private AviReaderEx m_avi_reader;
        [NonSerialized]
        private bool m_avi_reader_opened = false;
        [OptionalField]
        private bool m_position_fixed = false;
        [OptionalField]
        private Bitmap m_image;

        /// <summary>
        /// aviファイルのOpen時に許される失敗回数
        /// </summary>
        const int THRESHOLD_FAILED = 10;

        public bool IsXFixedAt( float time ) {
            float min, max;
            if ( mc_x.GetKeyMinMax( out min, out max ) ) {
                if ( min <= time && time <= max ) {
                    return true;
                }
            }
            return false;
        }

        public bool IsYFixedAt( float time ) {
            float min, max;
            if ( mc_y.GetKeyMinMax( out min, out max ) ) {
                if ( min <= time && time <= max ) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 描画オブジェクトの位置が固定されるかどうかを示す値を取得，または設定します
        /// </summary>
        public bool PositionFixed {
            get {
                return m_position_fixed;
            }
            set {
                m_position_fixed = value;
            }
        }

        [Browsable(false)]
        public Size ImageSize {
            get {
                if ( m_avi_mode ) {
                    if ( m_avi_reader.Opened ) {
                        return m_avi_reader.BitmapSize;
                    }
                } else {
                    if ( m_image != null ) {
                        return m_image.Size;
                    }
                }
                return new Size();
            }
        }

        public string GetAviPath() {
            if ( !m_avi_mode ) {
                return "";
            } else {
                string[] spl = m_avi_config.Split( "\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
                if ( spl.Length > 0 ) {
                    return spl[0];
                } else {
                    return "";
                }
            }
        }

        [Browsable(false)]
        public string AviConfig {
            get {
                return m_avi_config;
            }
        }

        [Browsable(false)]
        public bool IsAviMode {
            get {
                return m_avi_mode;
            }
        }

        public void SetAvi( string avi_config ) {
            m_avi_mode = true;
            m_avi_config = avi_config;
            string avi_file = GetAviPath();
            /*if ( m_bmp != null ) {
                m_bmp = null; //.Dispose();
            }*/
            if ( m_avi_reader != null ) {
                if ( m_avi_reader.Opened ) {
                    m_avi_reader.Close();
                    m_avi_reader = null;
                    m_avi_reader = new AviReaderEx();
                }
            } else {
                m_avi_reader = new AviReaderEx();
            }
            if ( m_avi_reader.NumFailed < THRESHOLD_FAILED ) {
                if ( File.Exists( avi_file ) ) {
                    m_avi_reader.Open( avi_file );
                    m_avi_reader_opened = true;
#if DEBUG
                    Common.DebugWriteLine( "TimeTable.SetAvi; avi_file exists" );
#endif
                }
            }
        }

        public void SetImage( Image img ) {
            /*if ( m_bmp != null ) {
                //m_bmp.Dispose();
                m_bmp = null;
            }*/
#if DEBUG
            Common.DebugWriteLine( "TimeTable.SetImage; before clone from argument \"img\"" );
            try {
                int i = image.Width;
            } catch {
            }
#endif
            if ( img != null ) {
                m_image = new Bitmap( img );
            }
#if DEBUG
            Common.DebugWriteLine( "TimeTable.SetImage; after cloned from argument \"img\"" );
            try {
                int i = image.Width;
            } catch {
            }
#endif
            m_avi_mode = false;
            if ( m_avi_reader_opened ) {
                if ( m_avi_reader.Opened ) {
                    m_avi_reader.Close();
                }
                m_avi_reader_opened = false;
            }
        }

        public Bitmap GetImage( float time ) {
#if DEBUG
            Common.DebugWriteLine( "TimeTable.GetImage(float); m_avi_mode=" + m_avi_mode );
#endif
            if ( m_avi_mode ) {
#if DEBUG
                Common.DebugWriteLine( "TimeTable.GetImage(float); m_avi_reader_opened=" + m_avi_reader_opened );
#endif
                if ( !m_avi_reader_opened ) {
                    if ( m_avi_reader == null ) {
                        m_avi_reader = new AviReaderEx();
                    }
                    if ( File.Exists( GetAviPath() ) ) {
                        try {
                            m_avi_reader.Open( GetAviPath() );
                            m_avi_reader_opened = m_avi_reader.Opened;
                        } catch {

                        }
                    }
                }
                if ( m_avi_reader_opened ) {
                    bool found = false;
                    float dt = 0f;
                    for ( int i = 0; i < list.Count; i++ ) {
                        if ( list[i].begin <= time && time <= list[i].end ) {
                            dt = time - list[i].begin;
                            found = true;
                            break;
                        }
                    }
#if DEBUG
                    Common.DebugWriteLine( "TimeTable.GetImage(float); found=" + found );
#endif
                    if ( found ) {
                        int frame = (int)((double)m_avi_reader.dwRate / (double)m_avi_reader.dwScale * dt);
                        if ( 0 <= frame && frame < m_avi_reader.CountFrames ) {
                            return m_avi_reader.Export( frame );
                        }
                    }
                }
                return null;
            } else {
                if ( m_image != null ) {
                    return m_image;
                } else {
                    return null;
                }
            }
        }

        public PointF GetPosition( float time ) {
            return new PointF( mc_x.GetValue( time ), mc_y.GetValue( time ) );
        }
        
        public float GetScale( float time ) {
            return mc_scale.GetValue( time );
        }
        
        public float GetAlpha( float time ) {
            float a = mc_alpha.GetValue( time );
            if ( a > 1f ) {
                a = 1f;
            } else if ( a < 0f ) {
                a = 0f;
            }
            return a;
        }
        
        public float GetRotate( float time ) {
            float r = mc_rotate.GetValue( time );
            if ( r > 360f ) {
                r = r % 360f;
            } else if ( r < 0f ) {
                r = r % 360f + 360f;
            }
            return r;
        }

        [OnDeserialized]
        private void onDeserialized( StreamingContext sc ) {
            if ( type == 3 ) {
                type = (int)TimeTableType.character;
            }

            if ( mc_x == null ) {
                mc_x = new BezierChain( Common.CURVE_X );
                mc_x.Default = position.X;
            }
            if ( mc_y == null ) {
                mc_y = new BezierChain( Common.CURVE_Y );
                mc_y.Default = position.Y;
            }
            if ( mc_scale == null ) {
                mc_scale = new BezierChain( Common.CURVE_SCALE );
                mc_scale.Default = scale;
            }
            if ( mc_alpha == null ) {
                mc_alpha = new BezierChain( Common.CURVE_ALPHA );
                mc_alpha.Default = 1.0f;
            }
            if ( mc_rotate == null ) {
                mc_rotate = new BezierChain( Common.CURVE_ROTATE );
            }
            mc_x.Color = Common.CURVE_X;
            mc_y.Color = Common.CURVE_Y;
            mc_scale.Color = Common.CURVE_SCALE;
            mc_alpha.Color = Common.CURVE_ALPHA;
            if ( image != null ) {
                m_image = new Bitmap( image );
                image.Dispose();
            }
        }

        public float Scale {
            get {
                return mc_scale.Default;
            }
            set {
                mc_scale.Default = value;
            }
        }
        
        /// <summary>
        /// 時刻nowにOnとなっているエントリが存在するかどうかを判定します
        /// </summary>
        /// <param name="now"></param>
        /// <returns></returns>
        public bool IsOn( float now ) {
            foreach ( TimeTableEntry entry in list ) {
                if ( entry.begin <= now && now <= entry.end ) {
                    return true;
                }
            }
            return false;
        }
        
        public void Dispose() {
            if ( list != null ) {
                list.Clear();
            }
            if ( image != null ) {
                image.Dispose();
            }
            if ( m_image != null ) {
                m_image.Dispose();
            }
            if ( mc_x != null ) {
                mc_x.Dispose();
            }
            if ( mc_y != null ) {
                mc_y.Dispose();
            }
            if ( mc_alpha != null ) {
                mc_alpha.Dispose();
            }
            if ( mc_rotate != null ) {
                mc_rotate.Dispose();
            }
            if ( mc_scale != null ) {
                mc_scale.Dispose();
            }
            if ( m_avi_reader != null ) {
                if ( m_avi_reader.Opened ) {
                    m_avi_reader.Close();
                }
            }
        }
        
        public void Remove( TimeTableEntry item ) {
            int index = -1;
            for ( int i = 0; i < list.Count; i++ ) {
                if ( list[i].begin == item.begin && list[i].end == item.end && list[i].body == item.body ) {
                    index = i;
                    break;
                }
            }
            if ( index >= 0 ) {
                list.RemoveAt( index );
            }
        }
        
        public object Clone() {
            TimeTable tmp = new TimeTable( stringValue, intValue, this.Type, m_image );
            for ( int i = 0; i < list.Count; i++ ) {
                tmp.list.Add( (TimeTableEntry)list[i].Clone() );
            }
            tmp.position = position;
            tmp.intValue = intValue;
            tmp.m_zOrder = m_zOrder;
            tmp.scale = scale;
            tmp.mc_alpha = (BezierChain)this.mc_alpha.Clone();
            tmp.mc_rotate = (BezierChain)this.mc_rotate.Clone();
            tmp.mc_scale = (BezierChain)this.mc_scale.Clone();
            tmp.mc_x = (BezierChain)this.mc_x.Clone();
            tmp.mc_y = (BezierChain)this.mc_y.Clone();
            tmp.m_avi_config = this.m_avi_config;
            tmp.m_avi_mode = this.m_avi_mode;
            tmp.m_avi_reader = new AviReaderEx();
            tmp.m_avi_reader_opened = false;
            tmp.m_position_fixed = this.m_position_fixed;
            return tmp;
        }

        [Browsable(false)]
        public TimeTableType Type {
            get {
                foreach ( TimeTableType t in Enum.GetValues( System.Type.GetType( "LipSync.TimeTableType" ) ) ) {
                    if ( (int)t == type ) {
                        return t;
                    }
                }
                return TimeTableType.none;
            }
            set {
                type = (int)value;
            }
        }

        public void Sort() {
            list.Sort();
        }
                
        public void RemoveAt( int entry ) {
            list.RemoveAt( entry );
        }
        
        public TimeTable( string stringValue, int intValue, TimeTableType type, Bitmap image )
            : this( stringValue, intValue, type, image, 1f ) {
        }

        public TimeTable( string stringValue, int intValue, TimeTableType type, Bitmap img, float scale ) {
            this.list = new List<TimeTableEntry>();
            this.stringValue = stringValue;
            this.type = (int)type;
            if ( img != null ) {
                m_image =(Bitmap)img.Clone();
            } else {
                m_image = null;
            }
            this.intValue = intValue;
            this.position = new Point( 0, 0 );
            this.scale = scale;
            this.m_zOrder = 0;
            mc_x = new BezierChain( Common.CURVE_X );
            mc_y = new BezierChain( Common.CURVE_Y );
            mc_scale = new BezierChain( Common.CURVE_SCALE );
            mc_scale.Default = 1f;
            mc_alpha = new BezierChain( Common.CURVE_ALPHA );
            mc_alpha.Default = 1f;
            mc_rotate = new BezierChain( Common.CURVE_ROTATE );
            m_avi_reader = new AviReaderEx();
            m_avi_reader_opened = false;
        }

        [Browsable(false)]
        public int ZOrder {
            get {
                return m_zOrder;
            }
            set {
                m_zOrder = value;
            }
        }

        [Browsable(false)]
        public int Value {
            get {
                return intValue;
            }
            set {
                intValue = value;
            }
        }

        public string Text {
            get {
                return stringValue;
            }
            set {
                stringValue = value;
            }
        }

        [Browsable(false)]
        public Bitmap Image {
            get {
                return m_image;
            }
        }

        [Browsable(false)]
        public int Count {
            get {
                return list.Count;
            }
        }

        public void Clear() {
            list.Clear();
        }

        [Browsable(false)]
        public TimeTableEntry this[int entry] {
            get {
                return list[entry];
            }
            set {
                list[entry] = (TimeTableEntry)value;
            }
        }

        public void Add( TimeTableEntry entry ) {
            list.Add( entry );
        }
        
        [Browsable(false)]
        public Point Position {
            get {
                return new Point( (int)mc_x.Default, (int)mc_y.Default );
            }
            set {
                mc_x.Default = value.X;
                mc_y.Default = value.Y;
            }
        }

        public Position Location {
            get {
                return new Position( mc_x.Default, mc_y.Default );
            }
            set {
                mc_x.Default = value.X;
                mc_y.Default = value.Y;
            }
        }
    }
    
}
