/*
    copyright (c) 2018 jones

    http://www.apache.org/licenses/LICENSE-2.0

    开源项目 https://github.com/jones2000/HQChart

    jones_2000@163.com

    图形扩展画法
*/ 
//日志
import { JSConsole } from "./umychart.console.wechat.js"

//行情数据结构体 及涉及到的行情算法(复权,周期等) 
import 
{
    ChartData, HistoryData,
    SingleData, MinuteData,
    Guid,
    ToFixedPoint,
    ToFixedRect,
} from "./umychart.data.wechat.js";

import 
{
    JSCommonCoordinateData,
    MARKET_SUFFIX_NAME
} from "./umychart.coordinatedata.wechat.js";

import 
{
    g_JSChartResource,
    JSCHART_LANGUAGE_ID,
    g_JSChartLocalization,
} from './umychart.resource.wechat.js'

import 
{
    IFrameSplitOperator,
} from './umychart.framesplit.wechat.js'

function IExtendChartPainting() 
{
    this.Canvas;                        //画布
    this.ChartBorder;                   //边框信息
    this.ChartFrame;                    //框架画法
    this.Name;                          //名称
    this.Data;  // = new ChartData();        //数据区
    this.ClassName = 'IExtendChartPainting';
    this.IsDynamic = false;
    this.IsEraseBG = false;               //是否每次画的时候需要擦除K线图背景
    this.IsAnimation=false;
    this.DrawAfterTitle = false;          //是否在动态标题画完以后再画,防止动态标题覆盖

    //上下左右间距
    this.Left = 5;
    this.Right = 5;
    this.Top = 5;
    this.Bottom = 5;

    this.Draw = function () { } //画图接口
    this.SetOption = function (option) { }  //设置参数接口
}

//K线Tooltip, 显示在左边或右边
function KLineTooltipPaint() 
{
    this.newMethod = IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.IsDynamic = true;
    this.IsEraseBG = true;
    this.DrawAfterTitle = true;
    this.ClassName = 'KLineTooltipPaint';
    this.LatestPoint;               //手势位置
    this.ShowPosition=0;            //显示位置 0=左 1=右

    this.BorderColor = g_JSChartResource.TooltipPaint.BorderColor;    //边框颜色
    this.BGColor = g_JSChartResource.TooltipPaint.BGColor;            //背景色
    this.TitleColor = g_JSChartResource.TooltipPaint.TitleColor;      //标题颜色
    this.Font = [g_JSChartResource.TooltipPaint.TitleFont];
    this.Mergin={ Left:2, Top:3, Bottom:5, Right:5 };
    this.ExtendLineWidth=5;

    this.Width = 50;
    this.Height = 100;
    this.LineHeight = 15; //行高
    this.LineSpace=2;   //行间距

    this.Left = 1;
    this.Top = 0;

    this.HQChart;
    this.KLineTitlePaint;
    this.IsHScreen = false;   //是否横屏
    this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;

    this.GetLeft = function () 
    {
        if (this.IsHScreen) 
        {
            return this.ChartBorder.GetRightEx()-this.Height-this.Top;
        }
        else
        {
            if (this.ShowPosition==0)
                return this.ChartBorder.GetLeft()+this.Left;
            else 
                return this.ChartBorder.GetRight()-this.Width-this.Left;
        }
    }

    this.GetTop = function () 
    {
        if (this.IsHScreen) 
        {
            if (this.ShowPosition==0)
                return this.ChartBorder.GetTop()+this.Left;
            else
                return this.ChartBorder.GetBottom()-this.Width-this.Left;
        }
        else
        {
            return this.ChartBorder.GetTopEx()+this.Top;
        }
    }

    //是否显示
    this.IsEnableDraw=function()
    {
        if (!this.HQChart || !this.HQChart.TitlePaint || !this.HQChart.TitlePaint[0]) return false;

        if (this.HQChart.EnableClickModel)
        {
            if (this.HQChart.ClickModel.IsShowCorssCursor===false) return false;
        }
        else if (!this.HQChart.IsOnTouch) 
        {
            return false;
        }

        return true;
    }

    this.Draw = function () 
    {
        if (!this.IsEnableDraw()) return;

        this.IsHScreen=this.ChartFrame.IsHScreen===true;
        this.KLineTitlePaint = this.HQChart.TitlePaint[0];
        var klineData = this.KLineTitlePaint.GetCurrentKLineData();
        if (!klineData) return;

        var titleData=this.GetFormatTitle({Data:klineData});
        if (!titleData || !IFrameSplitOperator.IsNonEmptyArray(titleData.AryText)) return;

        this.CalculateTooltipSize(titleData);
        this.CalculateShowPosition();
        this.DrawBG();
        this.DrawTooltipData(titleData);
        this.DrawBorder();
    }

    //[{ Text:, Color, Title:, TitleColor, }]
    this.GetFormatTitle=function(data)
    {
        if (!data || !data.Data) return;

        var item=data.Data;
        var upperSymbol;
        if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
        var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        var aryText=[];
        var result={ AryText:aryText };
        var text, title, color;

        text=IFrameSplitOperator.FormatDateString(item.Date);
        aryText.push({ Text:text, Color:this.TitleColor });

        var period = this.HQChart.Period;
        if (ChartData.IsMinutePeriod(period, true) && IFrameSplitOperator.IsNumber(item.Time))
        {
            text = this.HQChart.FormatTimeString(item.Time);
            aryText.push({ Text:text, Color:this.TitleColor });
        }
        else if (ChartData.IsSecondPeriod(period) && IFrameSplitOperator.IsNumber(item.Time))
        {
            text = this.HQChart.FormatTimeString(item.Time,"HH:MM:SS");
            aryText.push({ Text:text, Color:this.TitleColor });
        }
        
        if (IFrameSplitOperator.IsNumber(item.Open))   //开
        {
            title = g_JSChartLocalization.GetText('Tooltip-Open', this.LanguageID);
            var color = this.KLineTitlePaint.GetColor(item.Open, item.YClose);
            text = item.Open.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }

        if (IFrameSplitOperator.IsNumber(item.High))    //高
        {
            title=g_JSChartLocalization.GetText('Tooltip-High',this.LanguageID);
            color=this.KLineTitlePaint.GetColor(item.High,item.YClose);
            text=item.High.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }

        if (IFrameSplitOperator.IsNumber(item.Low))    //低
        {
            title=g_JSChartLocalization.GetText('Tooltip-Low',this.LanguageID);
            color=this.KLineTitlePaint.GetColor(item.Low,item.YClose);
            text=item.Low.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }

        if (IFrameSplitOperator.IsNumber(item.Close))    //收
        {
            title=g_JSChartLocalization.GetText('Tooltip-Close',this.LanguageID);
            color=this.KLineTitlePaint.GetColor(item.Close,item.YClose);
            text=item.Close.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }
       
        //涨幅
        title=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID);
        if (item.YClose>0)
        {
            var value = (item.Close - item.YClose) / item.YClose * 100;
            color = this.KLineTitlePaint.GetColor(value, 0);
            text = value.toFixed(2) + '%';
        }
        else
        {
            text='--.--';
            color = this.KLineTitlePaint.GetColor(0, 0);
        }
        aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });


        if (IFrameSplitOperator.IsNumber(item.Vol))
        {
            title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
            text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }

        if (IFrameSplitOperator.IsNumber(item.Amount))
        {
            title = g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID);
            text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }

        //持仓量
        if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) 
        {
            title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
            text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }

        return result;
    }

    this.CalculateTooltipSize=function(titleData)
    {
        this.Canvas.font=this.Font[0];
        this.LineHeight=this.Canvas.measureText("擎").width;
        var height=0;
        var maxTitleWidth=0, maxTextWidth=0, maxLineWidth=0;
        for(var i=0; i<titleData.AryText.length; ++i)
        {
            var item=titleData.AryText[i];

            if (height>0) height+=this.LineSpace;

            var lineWidth=0;
            if (item.Title)
            {
                var textWidth=this.Canvas.measureText(item.Title).width+2;
                if (maxTitleWidth<textWidth) maxTitleWidth=textWidth;
                lineWidth+=textWidth;
            }

            if (item.Text)
            {
                var textWidth=this.Canvas.measureText(item.Text).width+2;
                if (maxTextWidth<textWidth) maxTextWidth=textWidth;
                lineWidth+=textWidth;
            }

            if (maxLineWidth<lineWidth) maxLineWidth=lineWidth;

            height+=this.LineHeight;
        }

        this.Height=height+(this.Mergin.Top+this.Mergin.Bottom);
        this.Width=(maxLineWidth)+(this.Mergin.Left+this.Mergin.Right)+this.ExtendLineWidth;

        return { Height:this.Height, Width:this.Width, MaxTitleWidth:maxTitleWidth, MaxTextWidth:maxTextWidth };
    }

    //判断显示位置
    this.CalculateShowPosition=function()
    {
        this.ShowPosition=0;
        if (!this.LatestPoint) return;

        if(this.IsHScreen)
        {
            var top=this.ChartBorder.GetTop();
            var height=this.ChartBorder.GetHeight();
            var yCenter=top+height/2;
            if (this.LatestPoint.Y<yCenter) this.ShowPosition=1;
        }
        else
        {
            var left=this.ChartBorder.GetLeft();
            var width=this.ChartBorder.GetWidth();
            var xCenter=left+width/2;
            if (this.LatestPoint.X<xCenter) this.ShowPosition=1;
        }
    }

    this.DrawBorder = function () 
    {
        var isHScreen = (this.ChartFrame.IsHScreen === true);
        var left = this.GetLeft();
        var top = this.GetTop();
        this.Canvas.strokeStyle = this.BorderColor;
        if (isHScreen)
        { 
            this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top), 
                this.HQChart.ToFixedRect(this.Height), this.HQChart.ToFixedRect(this.Width));
        }
        else 
        {
            this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top), 
                this.HQChart.ToFixedRect(this.Width), this.HQChart.ToFixedRect(this.Height));
        }
    }

    this.DrawBG = function () 
    {
        var isHScreen = (this.ChartFrame.IsHScreen === true);
        var left = this.GetLeft();
        var top = this.GetTop();
        this.Canvas.fillStyle = this.BGColor;
        if (isHScreen) this.Canvas.fillRect(left, top, this.Height, this.Width);
        else this.Canvas.fillRect(left, top, this.Width, this.Height);
    }

    this.DrawTooltipData = function (titleData) 
    {
        //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
        var left = this.GetLeft();
        var top = this.GetTop();

        if (this.IsHScreen) 
        {
            this.Canvas.save();
            var x = this.GetLeft() + this.Height, y = this.GetTop();

            this.Canvas.translate(x, y);
            this.Canvas.rotate(90 * Math.PI / 180);

            //x, y 作为原点
            left =0;
            top = 0;
        }

        this.Canvas.textBaseline="top";
        var right=left+this.Width-this.Mergin.Right;
        left+=this.Mergin.Left;
        top+=this.Mergin.Top;

        for(var i=0; i<titleData.AryText.length; ++i)
        {
            var item=titleData.AryText[i];

            if (item.Title)
            {
                this.Canvas.textAlign="left";
                this.Canvas.fillStyle=item.TitleColor;
                this.Canvas.fillText(item.Title,left,top);
            }

            if (item.Text)
            {
                this.Canvas.textAlign="right";
                this.Canvas.fillStyle=item.Color;
                this.Canvas.fillText(item.Text,right,top);
            }

            top+=this.LineHeight+this.LineSpace;
        }

        if (this.IsHScreen) this.Canvas.restore();
    }

    //设置参数接口
    this.SetOption = function (option) 
    {
        if (option.LineHeight > 0) this.LineHeight = option.LineHeight;
        if (option.BGColor) this.BGColor = option.BGColor;
        if (option.LanguageID > 0) this.LanguageID = option.LanguageID;
    }
}

function MinuteTooltipPaint() 
{
    this.newMethod = KLineTooltipPaint;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName = 'MinuteTooltipPaint';
    this.IsShowAveragePrice=true;

    this.GetTop=function()
    {
        if (this.IsHScreen) 
        {
            if (this.ShowPosition==0)
                return this.ChartBorder.GetTop()+this.Left;
            else
                return this.ChartBorder.GetBottom()-this.Width-this.Left;
        }
        else
        {
            return this.ChartBorder.GetTop()+this.Top;
        }
    }

    this.GetLeft=function()
    {
        if (this.IsHScreen) 
        {
            return this.ChartBorder.GetRight()-this.Height-this.Top;
        }
        else
        {
            if (this.ShowPosition==0)
                return this.ChartBorder.GetLeft()+this.Left;
            else 
                return this.ChartBorder.GetRight()-this.Width-this.Left;
        }
    }

    this.GetFormatTitle=function(data)
    {
        if (!data || !data.Data) return;
        var item=data.Data;
        var upperSymbol;
        if (this.HQChart.Symbol) upperSymbol=this.HQChart.Symbol.toUpperCase();
        var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        this.YClose = this.KLineTitlePaint.YClose;

        var aryText=[];
        var result={ AryText:aryText };
        var text, title, color, value;

        if (IFrameSplitOperator.IsNumber(item.Date))
        {
            text=IFrameSplitOperator.FormatDateString(item.Date);
            aryText.push({ Text:text, Color:this.TitleColor });
        }

        if (IFrameSplitOperator.IsNumber(item.Time))
        {
            text=IFrameSplitOperator.FormatTimeString(item.Time);
            aryText.push({ Text:text, Color:this.TitleColor });
        }

        if (IFrameSplitOperator.IsNumber(item.Close))    //最新
        {
            title = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
            color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
            text = item.Close.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }
        
        if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true)    //均价
        {
            title = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
            color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
            text = item.AvPrice.toFixed(defaultfloatPrecision);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }

        if (IFrameSplitOperator.IsNumber(item.Close) && IFrameSplitOperator.IsNumber(this.YClose))   //涨幅
        {
            title = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
            value = (item.Close - this.YClose) / this.YClose * 100;
            color = this.KLineTitlePaint.GetColor(value, 0);
            text = value.toFixed(2) + '%';
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
        }

        if (IFrameSplitOperator.IsNumber(item.Vol))
        {
            title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
            text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }
        
        if (IFrameSplitOperator.IsNumber(item.Amount))
        {
            title = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
            text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }
        
        if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) //持仓量
        {
            title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
            text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
            aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
        }
        
        return result;
    }

    /*
    this.DrawTooltipData = function (item) 
    {
        //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
        var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        var left = this.GetLeft() + 2;
        var top = this.GetTop() + 3;
        this.YClose = this.KLineTitlePaint.YClose;

        if (this.IsHScreen) 
        {
            this.Canvas.save();
            var x = this.GetLeft() + this.Height, y = this.GetTop();

            this.Canvas.translate(x, y);
            this.Canvas.rotate(90 * Math.PI / 180);

            //x, y 作为原点
            left = 2;
            top = 3;
        }

        this.Canvas.textBaseline = "top";
        this.Canvas.textAlign = "left";
        this.Canvas.font = this.Font[0];
        var labelWidth = this.Canvas.measureText('擎: ').width;

        var aryDateTime = item.DateTime.split(' ');
        if (aryDateTime && aryDateTime.length == 2)
        {
            var text = this.HQChart.FormatDateString(aryDateTime[0]);
            this.Canvas.fillStyle = this.TitleColor;
            this.Canvas.fillText(text, left, top);

            top += this.LineHeight;
            text = this.HQChart.FormatTimeString(aryDateTime[1]);
            this.Canvas.fillText(text, left, top);
        }

        top += this.LineHeight;
        this.Canvas.fillStyle = this.TitleColor;
        text = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
        this.Canvas.fillText(text, left, top);
        var color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
        text = item.Close.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle = color;
        this.Canvas.fillText(text, left + labelWidth, top);

        if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true)
        {
            top += this.LineHeight;
            this.Canvas.fillStyle = this.TitleColor;
            text = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
            this.Canvas.fillText(text, left, top);
            var color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
            var text = item.AvPrice.toFixed(defaultfloatPrecision);
            this.Canvas.fillStyle = color;
            this.Canvas.fillText(text, left + labelWidth, top);
        }

        top += this.LineHeight;
        this.Canvas.fillStyle = this.TitleColor;
        text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
        this.Canvas.fillText(text, left, top);
        var value = (item.Close - this.YClose) / this.YClose * 100;
        var color = this.KLineTitlePaint.GetColor(value, 0);
        var text = value.toFixed(2) + '%';
        this.Canvas.fillStyle = color;
        this.Canvas.fillText(text, left + labelWidth, top);

        if (IFrameSplitOperator.IsNumber(item.Vol))
        {
            this.Canvas.fillStyle = this.TitleColor;
            top += this.LineHeight;
            text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
            this.Canvas.fillText(text, left, top);
            var text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
            this.Canvas.fillText(text, left + labelWidth, top);
        }
        
        if (IFrameSplitOperator.IsNumber(item.Amount))
        {
            top += this.LineHeight;
            text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
            this.Canvas.fillText(text, left, top);
            var text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
            this.Canvas.fillText(text, left + labelWidth, top);
        }

        //持仓量
        var upperSymbol;
        if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
        if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) 
        {
            this.Canvas.fillStyle = this.TitleColor;
            top += this.LineHeight;
            text = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
            this.Canvas.fillText(text, left, top);
            var text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
            this.Canvas.fillText(text, left + labelWidth, top);
        }
        

        if (this.IsHScreen) this.Canvas.restore();
    }
    */
}


//////////////////////////////////////////////////////////////////////////////
// 弹幕
//弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 }
function BarrageList() 
{
    this.PlayList = [];   //正在播放队列
    this.Cache = [];      //没有播放的弹幕数据
    this.MinLineHeight = 40;
    this.Height;        //高度
    this.Step = 1;

    //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 }
    this.GetPlayList = function (obj) 
    {
        var canves = obj.Canves;
        var right = obj.Right;
        var left = obj.Left;
        var width = right - left;
        var isMoveStep = obj.IsMoveStep;

        var list = [];
        var yOffset = 0;
        for (var i = 0; i < this.PlayList.length; ++i) 
        {
            var ary = this.PlayList[i];
            var lineHeight = this.MinLineHeight;
            if (ary.Height > this.MinLineHeight) lineHeight = ary.Height;

            var bAddNewItem = true;  //是否需要加入新弹幕
            var bRemoveFirst = false; //是否删除第1个数据
            for (var j = 0; j < ary.Data.length; ++j) 
            {
                var item = ary.Data[j];
                var playItem = { X: item.X, Y: yOffset, Text: item.Text, Color: item.Color, Height: lineHeight, Font: item.Font, Info: item.Info };
                list.push(playItem);

                if (!isMoveStep) continue;

                if (j == ary.Data.length - 1 && this.Cache.length > 0)    //最后一个数据了 判断是否需要增加弹幕
                {
                    bAddNewItem = false;
                    if (!item.TextWidth) 
                    {
                        if (item.Font && item.Font.Name) canves.font = item.Font.Name;
                        else canves.font = obj.Font;
                        item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
                    }

                    if (item.X >= item.TextWidth)
                        bAddNewItem = true;
                }
                else if (j == 0) 
                {
                    bRemoveFirst = false;
                    if (!item.TextWidth) 
                    {
                        if (item.Font && item.Font.Name) canves.font = item.Font.Name;
                        else canves.font = obj.Font;
                        item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
                    }

                    if (item.X > width + item.TextWidth) bRemoveFirst = true;
                }

                item.X += this.Step;
            }

            if (isMoveStep && bAddNewItem && this.Cache.length > 0)    //最后一个数据了 判断是否需要增加弹幕
            {
                var cacheItem = this.Cache.shift();
                var newItem = { X: 0, Text: cacheItem.Text, Color: cacheItem.Color, Font: cacheItem.Font, Info: cacheItem.Info };
                ary.Data.push(newItem);
            }

            if (isMoveStep && bRemoveFirst && ary.Data.length > 0) 
            {
                var removeItem = ary.Data.shift();
                this.OnItemPlayEnd(obj.HQChart, removeItem);
            }

            yOffset += lineHeight;
        }

        return list;
    }

    //根据高度计算播放队列个数
    this.CacluatePlayLine = function (height) 
    {
        this.Height = height;
        var lineCount = parseInt(height / this.MinLineHeight);
        if (this.PlayList.length < lineCount)
         {
            var addCount = lineCount - this.PlayList.length;
            for (var i = 0; i < addCount; ++i) 
            {
                this.PlayList.push({ Data: [] });
            }
        }
        else if (this.PlayList.length > lineCount) 
        {
            var removeCount = this.PlayList.length - lineCount;
            for (var i = 0; i < removeCount; ++i) 
            {
                var ary = this.PlayList.pop();
                for (var j = 0; j < ary.Data.length; ++j) 
                {
                    var item = ary.Data[j];
                    var cacheItem = { Text: item.Text, Color: item.Color, Font: item.Font, Info: item.Info };
                    this.Cache.unshift(cacheItem);
                }
            }
        }

        JSConsole.Chart.Log(`[BarrageList::CacluatePlayLine] LineCount=${this.PlayList.length} Height=${this.Height}`)
    }

    //添加弹幕
    this.AddBarrage = function (barrageData) 
    {
        for (var i in barrageData) {
            var item = barrageData[i];
            this.Cache.push(item);
        }
    }

    this.OnItemPlayEnd = function (hqChart, item)  //单挑弹幕播放完毕
    {
        //监听事件
        var event = hqChart.GetBarrageEvent();
        if (!event || !event.Callback) return;

        event.Callback(event, item, this);
    }

    this.Count = function () { return this.Cache.length; } //未播放的弹幕个数
}

//背景图 支持横屏
function BackgroundPaint() 
{
    this.newMethod = IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName = 'BackgroundPaint';

    this.IsDynamic = false;
    this.IsCallbackDraw = true;   //在回调函数里绘制, 不在Draw()中绘制

    this.FrameID = 0;
    this.Data;      //背景数据 { Start:, End:, Color:[] }
    this.ID = Guid(); //唯一的ID

    /*
    this.Data=
    [ 
        { Start:{ Date:20181201 }, End:{ Date:20181230 }, Color:'rgb(44,55,44)' } , 
        { Start:{ Date:20190308 }, End:{ Date:20190404 }, Color:['rgb(44,55,255)','rgb(200,55,255)'] } 
    ]
    */

    this.ChartSubFrame;
    this.ChartBorder;
    this.KData;
    this.Period;
    this.XPointCount = 0;

    this.SetOption = function (option) //设置
    {
        if (option.FrameID > 0) this.FrameID = option.FrameID;
        if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID = option.ID;
    }

    this.Draw = function () 
    {
        if (!this.Data || !this.HQChart) return;
        if (!this.ChartFrame || !this.ChartFrame.SubFrame || this.ChartFrame.SubFrame.length <= this.FrameID) return;
        var klineChart = this.HQChart.ChartPaint[0];
        if (!klineChart || !klineChart.Data) return;

        this.ChartSubFrame = this.ChartFrame.SubFrame[this.FrameID].Frame;
        this.ChartBorder = this.ChartSubFrame.ChartBorder;
        this.KData = klineChart.Data;
        this.Period = this.HQChart.Period;
        if (!this.KData || this.KData.Data.length <= 0) return;

        var isHScreen = (this.ChartSubFrame.IsHScreen === true);
        this.XPointCount = this.ChartSubFrame.XPointCount;
        var xPointCount = this.ChartSubFrame.XPointCount;

        var firstKItem = this.KData.Data[this.KData.DataOffset];
        var endIndex = this.KData.DataOffset + xPointCount - 1;
        if (endIndex >= this.KData.Data.length) endIndex = this.KData.Data.length - 1;
        var endKItem = this.KData.Data[endIndex];
        var showData = this.GetShowData(firstKItem, endKItem);
        if (!showData || showData.length <= 0) return;

        var kLineMap = this.BuildKLineMap();
        var bottom = this.ChartBorder.GetBottomEx();
        var top = this.ChartBorder.GetTopEx();
        var height = this.ChartBorder.GetHeightEx();
        if (isHScreen) 
        {
            top = this.ChartBorder.GetRightEx();
            bottom = this.ChartBorder.GetLeftEx();
            height = this.ChartBorder.GetWidthEx();
        }

        for (var i in showData) 
        {
            var item = showData[i];
            var rt = this.GetBGCoordinate(item, kLineMap);
            if (!rt) continue;

            if (Array.isArray(item.Color)) 
            {
                var gradient;
                if (isHScreen) gradient = this.Canvas.createLinearGradient(bottom, rt.Left, top, rt.Left);
                else gradient = this.Canvas.createLinearGradient(rt.Left, top, rt.Left, bottom);
                var offset = 1 / item.Color.length;
                for (var i in item.Color) 
                {
                    gradient.addColorStop(i * offset, item.Color[i]);
                }
                this.Canvas.fillStyle = gradient;
            }
            else 
            {
                this.Canvas.fillStyle = item.Color;
            }

            if (isHScreen) this.Canvas.fillRect(ToFixedRect(bottom), ToFixedRect(rt.Left), ToFixedRect(height), ToFixedRect(rt.Width));
            else this.Canvas.fillRect(ToFixedRect(rt.Left), ToFixedRect(top), ToFixedRect(rt.Width), ToFixedRect(height));
        }
    }

    this.GetShowData = function (first, end) 
    {
        var aryData = [];
        for (var i in this.Data) {
            var item = this.Data[i];
            var showItem = {};
            if (item.Start.Date >= first.Date && item.Start.Date <= end.Date) showItem.Start = item.Start;
            if (item.End.Date >= first.Date && item.End.Date <= end.Date) showItem.End = item.End;
            if (showItem.Start || showItem.End) 
            {
                showItem.Color = item.Color;
                aryData.push(showItem);
            }
        }

        return aryData;
    }

    this.BuildKLineMap = function () 
    {
        var isHScreen = (this.ChartSubFrame.IsHScreen === true);
        var dataWidth = this.ChartSubFrame.DataWidth;
        var distanceWidth = this.ChartSubFrame.DistanceWidth;
        var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin;
        if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin;
        var chartright = this.ChartBorder.GetRight();
        if (isHScreen) chartright = this.ChartBorder.GetBottom();

        var mapKLine = { Data: new Map() }; //Key: date / date time, Value:索引
        for (var i = this.KData.DataOffset, j = 0; i < this.KData.Data.length && j < this.XPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth)) 
        {
            var kItem = this.KData.Data[i];
            var left = xOffset;
            var right = xOffset + dataWidth;
            if (right > chartright) break;
            var x = left + (right - left) / 2;

            if (j == 0) mapKLine.XLeft = left;
            mapKLine.XRight = right;

            var value = { Index: i, ShowIndex: j, X: x, Right: right, Left: left, Date: kItem.Date };
            if (ChartData.IsMinutePeriod(this.Period, true)) 
            {
                var key = `Date:${kItem.Date} Time:${kItem.Time}`;
                value.Time = kItem.Time;
            }
            else 
            {
                var key = `Date:${kItem.Date}`;
            }

            mapKLine.Data.set(key, value);
        }

        return mapKLine;
    }

    this.GetBGCoordinate = function (item, kLineMap) 
    {
        var xLeft = null, xRight = null;
        if (item.Start) 
        {
            if (ChartData.IsMinutePeriod(this.Period, true))
                var key = `Date:${item.Start.Date} Time:${item.Start.Time}`;
            else
                var key = `Date:${item.Start.Date}`;

            if (kLineMap.Data.has(key)) 
            {
                var findItem = kLineMap.Data.get(key);
                xLeft = findItem.Left;
            }
            else 
            {
                for (var kItem of kLineMap.Data) 
                {
                    var value = kItem[1];
                    if (value.Date > item.Start.Date) 
                    {
                        xLeft = value.Left;
                        break;
                    }
                }
            }
        }
        else 
        {
            xLeft = kLineMap.XLeft;
        }

        if (item.End) 
        {
            if (ChartData.IsMinutePeriod(this.Period, true))
                var key = `Date:${item.End.Date} Time:${item.End.Time}`;
            else
                var key = `Date:${item.End.Date}`;

            if (kLineMap.Data.has(key)) 
            {
                var findItem = kLineMap.Data.get(key);
                xRight = findItem.Right;
            }
            else 
            {
                var previousX = null;
                for (var kItem of kLineMap.Data) 
                {
                    var value = kItem[1];
                    if (value.Date > item.End.Date) 
                    {
                        xRight = previousX;
                        break;
                    }
                    previousX = value.Right;
                }
            }
        }
        else 
        {
            xRight = kLineMap.XRight;
        }

        if (xLeft == null || xRight == null) return null;

        return { Left: xLeft, Right: xRight, Width: xRight - xLeft };
    }
}


//弹幕
function BarragePaint() 
{
    this.newMethod = IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName = 'BarragePaint';
    this.IsAnimation = true;
    this.IsEraseBG = true;
    this.HQChart;

    this.Font = g_JSChartResource.Barrage.Font;
    this.TextColor = g_JSChartResource.Barrage.Color;
    this.FontHeight = g_JSChartResource.Barrage.Height;

    this.BarrageList = new BarrageList();  //字幕列表
    this.IsMoveStep = false;

    
    this.SetOption = function (option) //设置参数接口
    {
        if (option) 
        {
            if (option.Step > 0) this.BarrageList.Step = option.Step;
            if (option.MinLineHeight) this.Barrage.MinLineHeight = option.MinLineHeight;
        }
    }

    this.DrawHScreen = function () 
    {
        var height = this.ChartBorder.GetWidth();
        var left = this.ChartBorder.GetTop();
        var right = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetRightEx();
        var wdith = this.ChartBorder.GetChartWidth();

        if (height != this.BarrageList.Height)
            this.BarrageList.CacluatePlayLine(height);

        this.Canvas.textBaseline = "middle";
        this.Canvas.textAlign = "left";

        var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
        this.IsMoveStep = false;
        if (!play) return;

        this.Canvas.save();
        this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0);
        this.Canvas.rotate(90 * Math.PI / 180);

        for (var i = 0; i < play.length; ++i) 
        {
            var item = play[i];
            if (item.Color) this.Canvas.fillStyle = item.Color;
            else this.Canvas.fillStyle = this.TextColor;
            if (item.Font) this.Canvas.font = item.Font.Name;
            else this.Canvas.font = this.Font;

            var fontHeight = this.FontHeight;
            if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
            var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);

            this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
        }

        this.Canvas.restore();
    }

    this.Draw = function () 
    {
        if (this.ChartFrame.IsHScreen) 
        {
            this.DrawHScreen();
            return;
        }

        var left = this.ChartBorder.GetLeft();
        var right = this.ChartBorder.GetRight();
        var top = this.ChartBorder.GetTopEx();
        var height = this.ChartBorder.GetHeight();

        if (height != this.BarrageList.Height)
            this.BarrageList.CacluatePlayLine(height);

        this.Canvas.textBaseline = "middle";
        this.Canvas.textAlign = "left";

        var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
        this.IsMoveStep = false;
        if (!play) return;

        for (var i = 0; i < play.length; ++i) 
        {
            var item = play[i];
            if (item.Color) this.Canvas.fillStyle = item.Color;
            else this.Canvas.fillStyle = this.TextColor;
            if (item.Font) this.Canvas.font = item.Font.Name;
            else this.Canvas.font = this.Font;

            var fontHeight = this.FontHeight;
            if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
            var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);

            this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
        }
    }
}

//导出统一使用JSCommon命名空间名
export
{
    IExtendChartPainting,
    KLineTooltipPaint,
    BarragePaint,
    MinuteTooltipPaint,
    BackgroundPaint,
}
/*
module.exports =
{
    JSCommonExtendChartPaint:
    {
        IExtendChartPainting: IExtendChartPainting,
        KLineTooltipPaint: KLineTooltipPaint,
        BarragePaint: BarragePaint,
        MinuteTooltipPaint: MinuteTooltipPaint,
        BackgroundPaint: BackgroundPaint,
    },

    //单个类导出
    JSCommonExtendChartPaint_IExtendChartPainting: IExtendChartPainting,
    JSCommonExtendChartPaint_KLineTooltipPaint: KLineTooltipPaint,
    JSCommonExtendChartPaint_BarragePaint: BarragePaint,
    JSCommonExtendChartPaint_MinuteTooltipPaint: MinuteTooltipPaint,
    JSCommonExtendChartPaint_BackgroundPaint: BackgroundPaint,
};
*/