/*
 * Telop.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.Runtime.Serialization;

using CurveEditor;

namespace LipSync {

    [Serializable]
    public class Telop : IComparable<Telop>, ICloneable, IDrawObject {
        string m_text = "";
        float m_start = 0f;
        float m_end = 0f;
        Font m_font = null;
        bool m_fadein = false;
        bool m_fadeout = false;
        float m_alpha = 1.0f;
        Point m_position = new Point( 0, 0 );
        Color m_fore_color;
        Size m_size;
        float m_fadein_ratio = 1f;
        float m_fadeout_ratio = 1f;
        [NonSerialized]
        object m_tag;
        [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;
        [OptionalField]
        private int m_id;
        [NonSerialized]
        public int Lane;
        [OptionalField]
        private bool m_position_fixed = false;
        [OptionalField]
        private int m_z_order;

        private static Color m_telop_default_color = Color.Black;
        private static Font m_default_font;

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

        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;
            }
        }

        public static void DecideLane( List<Telop> list ) {
            if ( list.Count <= 0 ) {
                AppManager.MaxTelopLanes = 0;
                return;
            }
            list.Sort();
            list[0].Lane = 0;
            int max = 0;
            for ( int i = 1; i < list.Count; i++ ) {
                int decided_lane = 0;
                for ( int index = 0; index < int.MaxValue; index++ ) {
                    decided_lane = index;
                    int count = 0;
                    for ( int j = 0; j < i; j++ ) {
                        if ( list[j].Lane == index ){
                            if ( Boare.Lib.AppUtil.Misc.IsOverwrapped( list[i].Start, list[i].End, list[j].Start, list[j].End ) ) {
                                count++;
                                break;
                            }
                        }
                    }
                    if ( count == 0 ) {
                        break;
                    }
                }
                max = Math.Max( max, decided_lane );
                list[i].Lane = decided_lane;
            }
            AppManager.MaxTelopLanes = max + 1;
        }

        [Browsable(false)]
        public int ID {
            get {
                return m_id;
            }
        }

        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 ( mc_x == null ) {
                mc_x = new BezierChain( Common.CURVE_X );
                mc_x.Default = m_position.X;
            }
            if ( mc_y == null ) {
                mc_y = new BezierChain( Common.CURVE_Y );
                mc_y.Default = m_position.Y;
            }
            if ( mc_scale == null ) {
                mc_scale = new BezierChain( Common.CURVE_SCALE );
                mc_scale.Default = 1f;
            }
            if ( mc_alpha == null ) {
                mc_alpha = new BezierChain( Common.CURVE_ALPHA );
                mc_alpha.Default = m_alpha;
            }
            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 ( m_font != null ) {
                m_size = Boare.Lib.AppUtil.Misc.MeasureString( m_text, m_font );
            }
        }

        public Telop( int id ) {
            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 );
            if ( m_default_font != null ) {
                m_font = (Font)m_default_font.Clone();
            } else {
                m_default_font = new Font( "MS UI Gothic", 18 );
                m_font = (Font)m_default_font.Clone();
            }
            m_size = Boare.Lib.AppUtil.Misc.MeasureString( m_text, m_font );
            m_id = id;
            m_fore_color = m_telop_default_color;
        }
        
        [Browsable( false )]
        public object Tag {
            get {
                return m_tag;
            }
            set {
                m_tag = value;
            }
        }

        public float FadeInSpan {
            get {
                return m_fadein_ratio;
            }
            set {
                m_fadein_ratio = value;
                if ( m_fadein_ratio <= 0f ) {
                    m_fadein_ratio = 1f;
                    m_fadein = false;
                }
            }
        }

        public float FadeOutSpan {
            get {
                return m_fadeout_ratio;
            }
            set {
                m_fadeout_ratio = value;
                if ( m_fadeout_ratio <= 0f ) {
                    m_fadeout_ratio = 1f;
                    m_fadeout = false;
                }
            }

        }
        
        [Browsable( false )]
        public Size ImageSize {
            get {
                return m_size;
            }
        }

        public int CompareTo( Telop item ) {
            if ( item.m_start < this.m_start ) {
                return 1;
            } else if ( item.m_start > this.m_start ) {
                return -1;
            } else {
                if ( item.m_end > this.m_end ) {
                    return 1;
                } else if ( item.m_end < this.m_end ) {
                    return -1;
                } else {
                    if ( item.m_id < this.m_id ) {
                        return 1;
                    } else if ( item.m_id > this.m_id ) {
                        return -1;
                    } else {
                        return 0;
                    }
                }
            }
        }

        [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;
            }
        }
        
        public Color Color {
            get {
                return m_fore_color;
            }
            set {
                m_fore_color = value;
                m_telop_default_color = value;
            }
        }

        /// <summary>
        /// IDを変更して実施するクローン
        /// </summary>
        /// <param name="change_id"></param>
        /// <returns></returns>
        public object Clone( int change_id ) {
            Telop result = new Telop( change_id );
            result.m_text = this.m_text;
            result.m_start = this.m_start;
            result.m_end = this.m_end;
            result.m_font = (Font)this.m_font.Clone();
            result.m_fadein = this.m_fadein;
            result.m_fadeout = this.m_fadeout;
            result.m_alpha = this.m_alpha;
            result.m_position = this.m_position;
            result.m_fore_color = this.m_fore_color;
            result.m_fadein_ratio = this.m_fadein_ratio;
            result.m_fadeout_ratio = this.m_fadeout_ratio;
            result.mc_alpha = (BezierChain)this.mc_alpha.Clone();
            result.mc_rotate = (BezierChain)this.mc_rotate.Clone();
            result.mc_scale = (BezierChain)this.mc_scale.Clone();
            result.mc_x = (BezierChain)this.mc_x.Clone();
            result.mc_y = (BezierChain)this.mc_y.Clone();
            result.Lane = this.Lane;
            result.m_position_fixed = this.m_position_fixed;
            result.m_size = this.m_size;
            return result;
        }

        /// <summary>
        /// 通常のクローニング操作
        /// </summary>
        /// <returns></returns>
        public object Clone() {
            return this.Clone( m_id );
        }

        public string Text {
            get {
                return m_text;
            }
            set {
                m_text = value;
                if ( m_font != null ) {
                    m_size = Boare.Lib.AppUtil.Misc.MeasureString( this.m_text, this.m_font );
                } else {
                    m_size = new Size();
                }
            }
        }

        public float Start {
            get {
                return m_start;
            }
            set {
                m_start = value;
            }
        }

        public float Length {
            get {
                return m_end - m_start;
            }
            set {
                m_end = m_start + value;
            }
        }

        [Browsable(false)]
        public float End {
            get {
                return m_end;
            }
            set {
                m_end = value;
            }
        }

        public Font Font {
            get {
                return m_font;
            }
            set {
                m_font = value;
                m_default_font = (Font)m_font.Clone();
                if ( m_font != null ) {
                    m_size = Boare.Lib.AppUtil.Misc.MeasureString( m_text, m_font );
                } else {
                    m_size = new Size();
                }
            }
        }
        
        public bool FadeIn {
            get {
                return m_fadein;
            }
            set {
                m_fadein = value;
            }
        }

        public bool FadeOut {
            get {
                return m_fadeout;
            }
            set {
                m_fadeout = value;
            }
        }

        public float Alpha {
            get {
                return m_alpha;
            }
            set {
                if ( value > 1.0f ) {
                    m_alpha = 1.0f;
                } else if ( value < 0.0f ) {
                    m_alpha = 0.0f;
                } else {
                    m_alpha = value;
                }
            }
        }
    }

}
