using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Policy;
using static System.Net.Mime.MediaTypeNames;
using System.Data.SqlTypes;
using System.Windows.Media.Animation;

namespace TrendCtrlII
{
    public enum EDRAGTYPE
    {
        SCREEN,
        ZOOM,
        UC,
        NONE
    }

    public enum ESCREENMODE
    {
        INIT,
        TEMPDATA,
        GRAPH
    }

    public enum EBUTTONTYPE
    {
        LEFTMON,
        RIGHTMON
    }

    public enum EALIGN
    {
        LEFT,
        CENTER,
        RIGHT
    }

    public partial class TrendCtrlII : UserControl
    {

        private BufferedGraphicsContext context;
        private BufferedGraphics bufferedGraphics;



        private ChartStyle _Style = new ChartStyle();
        public CMouseinfo Mouseinfo;    //마우스위치정보

        public Boolean Refresh_BackGround = true;
        public Boolean Refresh_Grpah = true;

        //화면이동모드
        public Boolean ScreenMove = false;

        //Boolean Make_BackT = false;

        Bitmap layer_bakT;  //전체영역의 배경
        Bitmap layer_bak;

        Bitmap layer_graph;
        Bitmap layer_graphT;    //전체영역의 그래프

        public float MaxValueY { get; set; } = 15;



        public Single LineWidth = 1;
        private Rectangle UserZoom;     //사용자줌영역

        //실값
        public Single[] values;
        // public int[] times;
        private CChinfo[] chinfo;

        public Boolean Key_Control = false;

        // private Rectangle Cursorrect;

        //2015-02-17 : 시간정보를 chinfo 에서 분리
        public Dictionary<UInt32, Int64> Time;

        public void RemoveAt(int idx)
        {
            //배열갯수를 벗어나면 처리하지 않는다.
            if (idx >= this.Time.Count) return;

            //삭제대상의 키를 확인한다.
            var timeKey = this.Time.Take(idx).First().Key;

            //시간정보삭제
            this.Time.Remove(timeKey);

            //채널정보 중 타임키를 공유하는 자료를 삭제한다.
            this.chinfo.ToList().ForEach(t => t.Value.Remove(timeKey));

        }

        public void AddData(long val_time, int ch, float val_volt, string ch_grp, string ch_title, Color ch_color)
        {
            //존재하지 않는 시간이라면 추가한다.
            var ttime = Time.Where(t => t.Value == val_time).FirstOrDefault();
            if (ttime.Key is default(UInt32) && ttime.Value is default(Int64)) // 기존에 값이 존재하지 않는경우
            {
                this.Time.Add((uint)this.Time.Count + 1, val_time);
                ttime = Time.Where(t => t.Value == val_time).FirstOrDefault();
            }

            //해당 채널이 존재하는지 확인한다
            var chdata = this.CHInfo.Where(t => t.CH == ch).FirstOrDefault();
            if (chdata == null)
            {
                Array.Resize(ref chinfo, chinfo.Length + 1);
                chdata = new CChinfo
                {
                    CH = ch,
                    Idx = (ushort)ch,
                    GROUP = ch_grp,
                    TITLE = ch_title,
                    Color = ch_color,
                    Show = true,
                };
                chinfo[chinfo.Length - 1] = chdata;
            }

            //해당 시간대의 데이터가 없다면 추가한다.
            if (chdata.Value.ContainsKey(ttime.Key) == false)
                chdata.Value.Add(ttime.Key, val_volt);

            RangeUpdateData = true;
        }

        public void AddData(long val_time, int ch, float val_volt)
        {
            //존재하지 않는 시간이라면 추가한다.
            var ttime = Time.Where(t => t.Value == val_time).FirstOrDefault();
            if (ttime.Key is default(UInt32))
            {
                this.Time.Add((uint)this.Time.Count + 1, val_time);
            }

            //해당 채널이 존재하는지 확인한다
            var chdata = this.CHInfo.Where(t => t.CH == ch).FirstOrDefault();
            if (chdata != null)
            {
                //해당 시간대의 데이터가 없다면 추가한다.
                if (chdata.Value.ContainsKey(ttime.Key) == false)
                    chdata.Value.Add(ttime.Key, val_volt);
            }
            RangeUpdateData = true;

        }

        private Boolean init;    //그리드초기화완료여부
        public String initmsg = "initializing...";
        public int initpercent = 0;

        public List<CUserCursor> uc = new List<CUserCursor>(0); //사용자커서 최대2개까지한다.

        //미정리

        //MY EVENT
        //public event OnClickProbeSensorHandlerL OnClickLEFT; //셀클릭
        //public delegate void OnClickProbeSensorHandlerL();       
        //public event OnClickProbeSensorHandlerR OnClickRIGHT; //셀클릭
        //public delegate void OnClickProbeSensorHandlerR();
        public event OnUpdateUserControlHandler OnUpdateUserCursor; //UPDATE USER CURSOR
        public delegate void OnUpdateUserControlHandler(int idx);


        //화면디자인관련      
        private Cursor cursor = Cursors.Default;    //현재커서의모양

        private StringBuilder Warn_msg = new StringBuilder("");   //경고메세지(화면 최상단중앙에 표시됨)
        private Font Warn_Font;

        //기타설정'
        public RectangleF WindowRect;  //머릿부분
        public RectangleF ChartRect;

        /// <summary>
        /// 현재데이터중 가장 마지막의 시간정보를 반환합니다. (없는경우 1900년도가 반화노딤)
        /// </summary>
        /// <returns></returns>
        public Int64 LastDate()
        {
            if (Time == null) return DateTime.Parse("1900-01-01").ToFileTime();
            if (Time.Count < 1) return DateTime.Parse("1900-01-01").ToFileTime();
            return Time.Last().Value;// [Time.Count - 1].Value;
        }

        public Int64 FirstDate()
        {
            if (Time == null) return DateTime.Parse("1900-01-01").ToFileTime();
            if (Time.Count < 1) return DateTime.Parse("1900-01-01").ToFileTime();
            return Time.First().Value;
        }

        public TrendCtrlII()
        {
            InitializeComponent();

            // Initialize Variables

            // Set Optimized Double Buffer to reduce flickering
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            // Redraw when resized
            this.SetStyle(ControlStyles.ResizeRedraw, true);

            this.Font = SystemInformation.MenuFont;
            this.Warn_Font = new Font("나눔고딕", 20, FontStyle.Bold);
            this.Warn_msg.Append("메세지가 없습니다");
            this.Mouseinfo = new CMouseinfo(new PointF(0, 0));
            Time = new Dictionary<uint, long>();


            // 더블 버퍼링을 위한 BufferedGraphicsContext 및 BufferedGraphics 초기화
            this.context = BufferedGraphicsManager.Current;
            bufferedGraphics = context.Allocate(this.CreateGraphics(), this.ClientRectangle);
            bufferedGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        }


        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (bufferedGraphics != null)
                {
                    bufferedGraphics.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        public ChartStyle Style
        {
            get { return this._Style; }
            set { this._Style = value; }
        }

        /// <summary>
        /// Draws the background gradient and the grid into Graphics <paramref name="g"/>
        /// </summary>
        /// <param name="g">Graphic</param>
        public void DrawBackgroundWindow(Graphics g, RectangleF crect, RectangleF winrect)
        {
            //WINDOW BORDER
            RectangleF borderrect = new RectangleF(winrect.Left, winrect.Top, winrect.Width - 2, winrect.Height - 2);
            g.DrawRectangle(Pens.LightGray, borderrect.Left, borderrect.Top, borderrect.Width, borderrect.Height);

            //CHART BORDER
            g.DrawRectangle(new Pen(Color.LightGray, 1), crect.Left, crect.Top, crect.Width, crect.Height);

        }

        public void Set_Refresh()
        {
            Console.WriteLine("Graph : Set_Refresh");
            Refresh_BackGround = true;
            Refresh_Grpah = true;
        }

        void DrawScreen_Nodata_With_Message(Graphics g, string Message)
        {
            //progress


            int bw = (int)(this.Width * 0.8);
            int bh = (int)(this.Height * 0.05);
            Rectangle rr = new Rectangle((this.Width - bw) / 2, (this.Height - bh) / 2 + 10, bw, bh);

            //Display Initial Message
            Font nf = new Font("나눔고딕", 20, FontStyle.Bold);
            Rectangle FullRect = new Rectangle(0, 0, this.Width, this.Height);
            SizeF initsize = g.MeasureString(initmsg, nf);
            g.DrawString(initmsg, nf, Brushes.Black, this.Width / 2 - initsize.Width / 2, this.Height / 2 - initsize.Height / 2 - bh);

            //Display progress Bar
            if (initpercent > 100) initpercent = 100;
            int perc = (int)(rr.Width * initpercent / 100);
            g.FillRectangle(Brushes.Green, new Rectangle(rr.Left, rr.Top, perc, rr.Height));

            //Display Control Border
            g.DrawRectangle(Pens.DarkGray, rr);
        }
        void DrawScreen_Chart(Graphics g)
        {
            Graphics g_bak;
            Graphics g_graph;

            //초기화전이다.
            if (this.layer_bak == null && this.layer_bakT == null) return;

            if (Style.XV1 == Style.X1 && Style.XV2 == Style.X2)
            {
                //전체영역이면(전체용 비트맵을 사용함)
                g_bak = Graphics.FromImage(this.layer_bakT);
                g_graph = Graphics.FromImage(this.layer_graphT);
            }
            else
            {
                g_bak = Graphics.FromImage(this.layer_bak);
                g_graph = Graphics.FromImage(this.layer_graph);
            }

            // g.CompositingMode = CompositingMode.SourceOver;

            if (Refresh_BackGround)
            {
                // g_bak.CompositingMode = CompositingMode.SourceOver;
                g_bak.Clear(Color.White);
                DrawBackgroundWindow(g_bak, ChartRect, WindowRect);
                Draw_Grid(g_bak, ChartRect);  //x축,y축의 눈금과 그리드표시
                Refresh_BackGround = false;
            }

            if (Refresh_Grpah)
            {
                // g_graph.CompositingMode = CompositingMode.SourceOver;
                g_graph.Clear(Color.Transparent);
                Draw_channel(g_graph, ChartRect, WindowRect);
                Refresh_Grpah = false;
            }

            g_bak.Dispose();
            g_graph.Dispose();

            //전체영역의경우 별도 할당된 이미지를 사용한ㄷ(최초 1번만 그리게된다)
            if (Style.XV1 == Style.X1 && Style.XV2 == Style.X2)
            {
                g.DrawImage(layer_bakT, 0, 0);
                g.DrawImage(layer_graphT, 0, 0);
            }
            else
            {
                g.DrawImage(layer_bak, 0, 0);
                g.DrawImage(layer_graph, 0, 0);
            }



            //기타정보그림
            Draw_ZoomSelect(g);
            Draw_Cursor(g, ChartRect);
            Draw_MouseInfo(g);
            Draw_Debug(g);
            if (ShowLegend) Draw_legend(g);
            g_bak.Dispose();
            g_graph.Dispose();

            //그리드는 미리 그려넣는다.
            //축범위 시작시간 : max(데이터영역,보이는영역)
            //축범위 종료시간 : min(데이터영역,보이는영역)
            //g.Clear(this.BackColor);

            var times = DateTime.FromFileTime(Style.X1);
            var timee = DateTime.FromFileTime(Style.X2);
            var rangeX = this.RangeX;
            var rangeY = this.RangeY;
            var rangeTimeS = DateTime.FromFileTime(this.Time[RangeX.min]);
            var rangeTimeE = DateTime.FromFileTime(this.Time[RangeX.max]);

            var DrawSX = Math.Max(times.ToFileTime(), rangeTimeS.ToFileTime());
            var DrawEX = Math.Min(timee.ToFileTime(), rangeTimeE.ToFileTime());
            var TimeSX = DateTime.FromFileTime(DrawSX);
            var TimeEX = DateTime.FromFileTime(DrawEX);
            var TermX = Math.Max(10, (TimeEX - TimeSX).TotalSeconds / 10f);

            var pitchX = ChartRect.Width / 10f;
            var pitchY = ChartRect.Height / 10f;
            //var borderPen = new Pen(Color.LightGray);// .FromArgb(150, 0xB0, 0xB0, 0xB0), 1);
            //borderPen.DashStyle = DashStyle.DashDot;
            //float x1, y1, x2, y2;
            //for (int r = 1; r < 10; r++)
            //{
            //    x1 = ChartRect.X;
            //    y1 = ChartRect.Y + r * pitchY;
            //    y2 = y1;
            //    x2 = ChartRect.Right;
            //    g.DrawLine(borderPen, x1, y1, x2, y2);

            //    for (int c = 1; c < 10; c++)
            //    {
            //        x1 = ChartRect.X + c * pitchX;
            //        y1 = ChartRect.Y;
            //        x2 = x1;
            //        y2 = ChartRect.Bottom;
            //        g.DrawLine(borderPen, x1, y1, x2, y2);
            //    }
            //}

            ////draw grid text (X axis)
            //for (int c = 1; c < 10; c++)
            //{
            //    x1 = ChartRect.X + c * pitchX;
            //    y1 = ChartRect.Y;
            //    x2 = x1;
            //    y2 = ChartRect.Bottom;
            //    g.DrawLine(borderPen, x1, y1, x2, y2);

            //    var valX = TimeSX.AddSeconds(c * TermX);
            //    {
            //        var txtrect = new RectangleF(x1 - pitchX / 2f, ChartRect.Bottom + 5, pitchX, 35);
            //        g.DrawString($"{valX:yy-MM-dd\nHH:mm:ss}", this.Font, Brushes.Black, txtrect, new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            //        g.DrawRectangle(Pens.Red, txtrect.X, txtrect.Y, txtrect.Width, txtrect.Height);
            //    }
            //}
            //for (int r = 1; r < 10; r++)
            //{
            //    x1 = ChartRect.X;
            //    y1 = ChartRect.Y + r * pitchY;
            //    y2 = y1;
            //    x2 = ChartRect.Right;

            //    var minY = rangeY.min;
            //    var maxY = rangeY.max;
            //    var TermY = Math.Max(0.1, (maxY - minY) / 10f); //전체 10조각
            //    var valY = minY + ((10-r) * TermY); //Y값은 반전시켜야 한다

            //    var txtrect = new RectangleF(ChartRect.Left-50, y1-20 , 50, 40);
            //    g.DrawString($"{valY:#0.0}v", this.Font, Brushes.Black, txtrect, new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            //    g.DrawRectangle(Pens.Red, txtrect.X, txtrect.Y, txtrect.Width, txtrect.Height);

            //}

            //borderPen.DashStyle = DashStyle.Solid;
            //g.DrawRectangle(borderPen, ChartRect.X, ChartRect.Y, ChartRect.Width, ChartRect.Height);
            //borderPen.Dispose();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"Startv View(Y) : {Style.YV1}~{Style.YV2}");
            sb.AppendLine($"Startv View(X) : {times}~{timee},{(timee - times).TotalHours}hours");
            sb.AppendLine($"Data Range(X) : {rangeTimeS}~{rangeTimeE}");
            sb.AppendLine($"Data Range(Y) : {rangeY.min}~{rangeY.max}");
            g.DrawString(sb.ToString(), this.Font, Brushes.Red, 100, 100);

            //g.DrawRectangle(Pens.Red, ChartRect.Left, ChartRect.Top, ChartRect.Width, ChartRect.Height);
            //g.DrawRectangle(Pens.Blue, LegendRect.Left, LegendRect.Top, LegendRect.Width, LegendRect.Height);
            //this.Update();
            //this.ResumeLayout();
        }
        /// Override OnPaint method
        protected override void OnPaint(PaintEventArgs e)
        {
            //base.OnPaint(e);
            DateTime start = DateTime.Now;

            //표시아이템이없다면 오류를 낸다.
            var g = bufferedGraphics.Graphics;
            g.Clear(this.BackColor);

            if (init == false)
            {
                DrawScreen_Nodata_With_Message(g, "최기화가 완료되지 않았습니다");
            }
            else if (chinfo == null)
            {
                DrawScreen_Nodata_With_Message(g, initmsg);
            }
            else
            {

                if (RangeUpdateData == true)
                {
                    var datas = CHInfo.Where(t => t.Value.Where(s => s.Key > 0).Any());
                    if (datas.Any())
                    {
                        //자료가 있는것을 대상으로 처리한다.
                        pre_minX = datas.Min(t => t.Value.Where(s => s.Key > 0).Min(v => v.Key));
                        pre_maxX = datas.Max(t => t.Value.Where(s => s.Key > 0).Max(v => v.Key));
                    }
                    else
                    {
                        pre_minX = pre_maxX = 0;
                    }

                    datas = CHInfo.Where(t => t.Value.Where(s => s.Value < MaxValueY && s.Value > -MaxValueY).Any());
                    if (datas.Any())
                    {
                        //100을 초과한 범위는 제한한다
                        pre_minY = datas.Min(t => t.Value.Where(s => s.Value < MaxValueY && s.Value > -MaxValueY).Min(u => u.Value));
                        pre_maxY = datas.Max(t => t.Value.Where(s => s.Value < MaxValueY && s.Value > -MaxValueY).Max(u => u.Value));
                    }
                    else
                    {
                        pre_minY = pre_maxY = 0;
                    }

                    RangeUpdateData = false;
                }

                DrawScreen_Chart(g);

            }
            bufferedGraphics.Render(e.Graphics);
            var ts = DateTime.Now - start;
            e.Graphics.DrawString($"refresh time:{ts.TotalMilliseconds}ms", this.Font, Brushes.Red, 10, 10);
        }

        public bool ShowLegend { get; set; } = false;
        void Draw_legend(Graphics g)
        {
            if (chinfo == null || chinfo.Any() == false) return;
            var maxcnt = 10;
            var h = LegendRect.Height / maxcnt;
            var w = LegendRect.Width;
            var showitem = this.chinfo.Where(t => t.Show).ToList();
            var itemcnt = Math.Min(10, showitem.Count());
            for (int i = 0; i < itemcnt; i++)
            {
                var y = LegendRect.Y + i * h;
                var item = showitem[i];
                var rect = new RectangleF(LegendRect.X, y, w, h);
                var rectflag = new RectangleF(rect.X, rect.Y, rect.Width * 0.15f, rect.Height);
                var rectttitle = new RectangleF(rectflag.Right + 3, rect.Y, rect.Width - rectflag.Width - 3, rect.Height);
                using (var bgcolor = new SolidBrush(item.Color))
                    g.FillRectangle(bgcolor, rectflag);
                var title = $"{item.TITLE}";
                if (item.Value.Any())
                    title = $"{item.TITLE} ({item.Value.Last().Value}v)";

                g.DrawString(title, this.Font, Brushes.Black, rectttitle, new StringFormat
                {
                    Alignment = StringAlignment.Near,
                    LineAlignment = StringAlignment.Center
                }); ;
                g.DrawRectangle(Pens.Black, rect.X, rect.Y, rect.Width, rect.Height);
            }
        }
        public void Draw_Cursor(Graphics g, RectangleF Crect)
        {


            if (this.uc.Count == 0) return;
            Font cfont = new Font("나눔고딕", 10, FontStyle.Bold);

            //커서는 기본 2개를 둔다(전체사이즈를 측정하기위해서 임시로 크기를 테스트한다.)
            String cursorstr = "▦ C1 2012-00-00- 00:00:00 ▦ C1 2012-00-00- 00:00:00 ▦ C1 2012-00-00- 00:00:00";
            SizeF FontSize = g.MeasureString(cursorstr.ToString(), cfont);

            //커서가표시될영역

            //Cursorrect = new Rectangle(0, 0, 50,50);
            //Cursorrect.X = (int)(Crect.Left + Crect.Width - FontSize.Width * 1.1) - 1;
            //Cursorrect.Y = (int)(Crect.Top - 2 - FontSize.Height);
            //Cursorrect.Width = (int)(FontSize.Width * 1.1);
            //Cursorrect.Height = (int)(FontSize.Height * 1.3);

            //g.FillRectangle(Brushes.DimGray, Cursorrect);
            //g.DrawRectangle(Pens.Gray, Cursorrect);

            //Single newy = Cursorrect.Top + ((Cursorrect.Height - FontSize.Height)/2) ;
            //Single Maxwidth = 0;

            //String fulltext = "";

            var qry = from CUserCursor cs in uc
                      orderby cs.Idx
                      select cs;


            cursorstr = "";
            foreach (CUserCursor uctl in qry)
            {
                Single newx = GetXfromTime(uctl.Time, Crect);
                uctl.Newx = newx;

                Single imgsizew = vmsnet.Properties.Resources.down_16.Width;
                Single imgsizeh = vmsnet.Properties.Resources.down_16.Height;

                cursorstr += " ▦ C" + (uctl.Idx + 1).ToString() + " " + GetTimeStr(uctl.Time, true, true, true, true);

                FontSize = g.MeasureString(cursorstr.ToString(), cfont);
                //  if (FontSize.Width > Maxwidth) Maxwidth = FontSize.Width;

                if (uctl.Idx < 1)
                {
                    g.DrawImage(vmsnet.Properties.Resources.down_16, (newx - imgsizew / 2) - 1, Crect.Top - imgsizeh - 5);
                    g.DrawLine(new Pen(Color.Green, 1), newx, Crect.Top, newx, Crect.Top + Crect.Height);

                    // fulltext = cursorstr;

                }
                else
                {
                    TimeSpan ts = DateTime.FromFileTime(uctl.Time) - DateTime.FromFileTime(uc[0].Time);
                    cursorstr += "  △t " + ts.Days.ToString() + "d " + ts.Hours.ToString() + ":" + ts.Minutes.ToString() + ":" + ts.Seconds.ToString();

                    g.DrawImage(vmsnet.Properties.Resources.down_orange, (newx - imgsizew / 2) - 1, Crect.Top - imgsizeh - 5);
                    g.DrawLine(new Pen(Color.Orange, 1), newx, Crect.Top, newx, Crect.Top + Crect.Height);
                    // if (fulltext == "") fulltext = cursorstr;
                    //  else fulltext += "\n" + cursorstr; 

                }
                //  newy += FontSize.Height + 1;
            }

            if (cursorstr != "")
            {
                FontSize = g.MeasureString(cursorstr, cfont);
                g.DrawString(cursorstr, cfont, Brushes.Black, Crect.Right - FontSize.Width - 2, Crect.Top - FontSize.Height - 2);
            }

            //
            // Cursorrect = new Rectangle((int)(ChartRect.Left + ChartRect.Width - Maxwidth), (int)ChartRect.Top, (int)Maxwidth, (int)(newy - FontSize.Height));
        }

        public void Draw_MouseInfo(Graphics g)
        {
            if (this.Mouseinfo.Showinfo)    //마웃위치정보를 화면에 표시를 한다.
            {
                String tm = GetTimeStr(Mouseinfo.Time, true, true, true, true) + "\n" + Mouseinfo.Volt.ToString("#0.00") + "v";
                if (Style.Show_DebugMsg) tm += "\n" + Mouseinfo.Time.ToString();

                SizeF tmsize = g.MeasureString(tm, Style._mouseinfofont);

                RectangleF rect = new RectangleF(Mouseinfo.Position.X + 5, Mouseinfo.Position.Y + 5, tmsize.Width + 10, tmsize.Height + 10);
                if (Mouseinfo.Position.X >= (this.ChartRect.Left + ChartRect.Width / 2))
                {
                    //마우스위치에따라서 표시정보 위치를 좌/우 교환한다  150223
                    rect = new RectangleF(Mouseinfo.Position.X - (tmsize.Width + 10) - 5, Mouseinfo.Position.Y + 5, tmsize.Width + 10, tmsize.Height + 10);
                }
                //g.FillRectangle(new SolidBrush(Color.FromArgb(140,Color.Green)), rect);
                g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width, rect.Height);

                g.DrawString(tm, Style._mouseinfofont, new SolidBrush(Style.design_mouseinfocolor), rect.Left + (rect.Width - tmsize.Width) / 2, rect.Top + (rect.Height - tmsize.Height) / 2);
            }
        }

        public void Draw_Grid(Graphics g, RectangleF crect)
        {

            //그리드는 무조건 10개로 쪼개서 표시한다.

            //설정된 표시영역
            DateTime SDate = DateTime.FromFileTime(Style.XV1);
            DateTime EDate = DateTime.FromFileTime(Style.XV2);

            //실제데이터영역
            DateTime Real_SDate = DateTime.FromFileTime(Time[RangeX.min]);
            DateTime Real_EDate = DateTime.FromFileTime(Time[RangeX.max]);

            var (normalizedStart, normalizedEnd) = NormalizeTimeRangeHalfHourIntervals(SDate, EDate);
            //SDVDate = normalizedStart;
            SDate = normalizedStart;
            EDate = normalizedEnd;

            SDate = Real_SDate > SDate ? Real_EDate : SDate;    //실제데이터가 보이는 영역보다 크다면 , 실데이터를 기준으로 한다.
            EDate = Real_EDate > EDate ? EDate : Real_SDate;    //실제데이터가 종료일자보다 크면 종료일자로 표시한다.

            TimeSpan TS = EDate - SDate;    //timespan

            var TermX = (int)((EDate - SDate).TotalSeconds / 10f);

            if (TS.TotalSeconds > Style.MaxZoomX)
            {
                //표시간격 최소 10초
                int tm = 10;

                if (Style.XGap == 0)
                {
                    tm = (int)(TS.TotalSeconds / 5);
                }
                else
                {
                    tm = Style.XGap;
                }

                var termcnt = (int)(TS.TotalSeconds / tm);
                if (termcnt < 10)
                {
                    tm = (int)(TS.TotalSeconds / 10f);
                }
                else if (termcnt > 10) //간격이 너무 크다
                {
                    tm = (int)(TS.TotalSeconds / 10f);
                }
                tm = GetClosestInterval(tm);

                for (Int64 i = 0; i <= TS.TotalSeconds; i += tm)
                {
                    DateTime NewDate = SDate.AddSeconds(i); //시작시간에서
                    if (SDate.Day != NewDate.Day)
                    {
                        //일자가 바귀엇으므로 해당 일즤 세로축을 표시
                        DateTime LineDay = DateTime.Parse(NewDate.ToString("yyyy-MM-dd 00:00:00"));
                        Single newx2 = GetXfromTime(LineDay.ToFileTime(), crect);
                        g.DrawLine(new Pen(Color.FromArgb(50, Color.Black)), newx2, crect.Top, newx2, crect.Top + crect.Height);
                        SDate = NewDate;
                    }

                    Single newx = GetXfromTime(NewDate.ToFileTime(), crect);
                    String timestr = "";

                    if (i == 0) timestr = GetDateTimeStr(NewDate.ToFileTime(), true);
                    else timestr = GetTimeStr(NewDate.ToFileTime(), i == 0 ? true : false, false);

                    timestr = NewDate.ToString("yy-MM-dd") + "\n" + NewDate.ToString("HH:mm:ss");

                    DrawString(g, timestr, Style.FontX, Brushes.Black, new PointF(newx, crect.Top + crect.Height + 5), EALIGN.CENTER);
                    if (i > 0) g.DrawLine(new Pen(Color.Gray), newx, crect.Top, newx, crect.Top + crect.Height);
                }

            }
            else
            {
                //Y축 값을 표시할 값이 없으면 오류를 표시한다.
                DrawString(g, "ERRX", Style.FontX, Brushes.Black, new PointF(crect.Left - 2, crect.Bottom), EALIGN.RIGHT, EALIGN.CENTER);
            }



            //세로(VOLT)표시
            //g.DrawString("start="+Style._startvolt.ToString(), this.Font, Brushes.Red, 100, 100);
            //g.DrawString("end="+Style._endvolt.ToString(), this.Font, Brushes.Red, 100, 120);

            //g.DrawString("최소="+Style.최소값.ToString(), this.Font, Brushes.Red, 100, 140); 
            //g.DrawString("최대="+Style.최대값.ToString(), this.Font, Brushes.Red, 100, 160);

            if (Math.Abs(Style.YV2 - Style.YV1) >= Style.MaxZoomY)
            {
                Single term;
                if (Style.YGap == 0)
                {
                    term = (Style.YV2 - Style.YV1) / 10;
                }
                else
                {
                    term = Style.YGap;
                }

                for (Single i = Style.YV1; i <= Style.YV2; i += term)
                {
                    // 세로길이 = 10v : 
                    Single newy = GetYfromVolt(i, crect);
                    if (Style.YV1 == 0 && i == 0)
                    {

                    }
                    else g.DrawLine(new Pen(Color.Gray), crect.Left, newy, crect.Left + crect.Width, newy);

                    //소수점없는경우에눈금표시한다.
                    DrawString(g, i.ToString("#0.00"), Style.FontY, Brushes.Black, new PointF(crect.Left - 2, newy), EALIGN.RIGHT, EALIGN.CENTER);
                }
            }
            else
            {
                //Y축 값을 표시할 값이 없으면 오류를 표시한다.
                DrawString(g, "ERRY", Style.FontY, Brushes.Black, new PointF(crect.Left - 2, crect.Top + 2), EALIGN.RIGHT, EALIGN.CENTER);
            }
        }

        public (DateTime normalizedStart, DateTime normalizedEnd) NormalizeTimeRangeHalfHourIntervals(DateTime start, DateTime end)
        {
            const int intervalInSeconds = 1800; // 30 minutes

            DateTime normalizedStart = NormalizeTimeToInterval(start, intervalInSeconds, roundDown: true);
            DateTime normalizedEnd = NormalizeTimeToInterval(end, intervalInSeconds, roundDown: false);

            return (normalizedStart, normalizedEnd);
        }

        private DateTime NormalizeTimeToInterval(DateTime time, int intervalInSeconds, bool roundDown)
        {
            long ticks = time.Ticks / TimeSpan.TicksPerSecond;
            if (roundDown)
            {
                ticks = (ticks / intervalInSeconds) * intervalInSeconds; // Round down
            }
            else
            {
                ticks = ((ticks + intervalInSeconds - 1) / intervalInSeconds) * intervalInSeconds; // Round up
            }
            return new DateTime(ticks * TimeSpan.TicksPerSecond);
        }


        private readonly int[] TimeIntervals = new int[]
           {
                1, 10, 30, 60, 300, 600, 1800, 3600, 21600, 43200, 86400, 432000, 864000, 2592000
           };
        public int GetClosestInterval(int seconds)
        {
            // Handle cases where seconds is less than the smallest interval or more than the largest interval
            if (seconds <= TimeIntervals[0])
                return TimeIntervals[0];
            if (seconds >= TimeIntervals[TimeIntervals.Length - 1])
                return TimeIntervals[TimeIntervals.Length - 1];

            // Find the closest interval
            int closestInterval = TimeIntervals[0];
            int minDifference = Math.Abs(seconds - closestInterval);

            foreach (int interval in TimeIntervals)
            {
                int difference = Math.Abs(seconds - interval);
                if (difference < minDifference)
                {
                    minDifference = difference;
                    closestInterval = interval;
                }
            }

            return closestInterval;
        }

        public void Draw_Debug(Graphics g)
        {
            if (!Style.Show_DebugMsg) return;

            //Single newy = 50;
            //String newstr = "";
            SizeF fontsize;

            StringBuilder DebugMsg = new StringBuilder();

            DebugMsg.AppendLine("Mouseinfo=" + this.Mouseinfo.Position.ToString() + " " + this.Mouseinfo.Showinfo.ToString());
            DebugMsg.AppendLine("Mouseinfo0=" + this.Mouseinfo.Position0.ToString() + " idx=" + this.Mouseinfo.DragIndex.ToString());
            DebugMsg.AppendLine("Mouse Volt =" + this.Mouseinfo.Volt.ToString() + " ,Time=" + DateTime.FromFileTime(Mouseinfo.Time).ToString());
            DebugMsg.AppendLine("Mouse Drag=" + Mouseinfo.Drag.ToString() + " " + Mouseinfo.DragType.ToString());
            DebugMsg.AppendLine("Mouse DragStart=" + Mouseinfo.DragStart.ToString());
            DebugMsg.AppendLine("TimeCount =" + Time.Count.ToString());

            TimeSpan NowTerm = DateTime.FromFileTime(Style.X2) - DateTime.FromFileTime(Style.X1);
            DebugMsg.AppendLine("XGap =" + Style.XGap.ToString() + ",YGap=" + Style.YGap.ToString() + ",Xterm=" + Style.X표시범위.ToString() + ",NowTerm=" + NowTerm.ToString());

            foreach (String file in Files)
            {
                DebugMsg.AppendLine("File = " + file);
            }
            foreach (CUserCursor ucitem in uc)
            {
                DebugMsg.AppendLine("uc" + ucitem.Idx.ToString() + ",x=" + ucitem.Newx.ToString() + ",time=" + ucitem.Time.ToString());
            }

            DebugMsg.AppendLine("======================");
            DebugMsg.AppendLine("시간축=" + Style.X1.ToString() + "~" + Style.X2.ToString() + " : " + DateTime.FromFileTime(Style.X1).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.X2).ToString("yy-MM-dd HH:mm:ss"));
            DebugMsg.AppendLine("시간축V=" + Style.XV1.ToString() + "/" + Style.XV2.ToString() + " : " + DateTime.FromFileTime(Style.XV1).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.XV2).ToString("yy-MM-dd HH:mm:ss"));
            DebugMsg.AppendLine("시간축VO=" + Style.XV1o.ToString() + "/" + Style.XV2o.ToString() + " : " + DateTime.FromFileTime(Style.XV1o).ToString("yy-MM-dd HH:mm:ss") + "~" + DateTime.FromFileTime(Style.XV2o).ToString("yy-MM-dd HH:mm:ss"));
            DebugMsg.AppendLine("======================");
            DebugMsg.AppendLine("전압축=" + Style.Y1.ToString() + "~" + Style.Y2.ToString());
            DebugMsg.AppendLine("전압축V=" + Style.YV1.ToString() + "~" + Style.YV2.ToString());
            DebugMsg.AppendLine("전압축VO=" + Style.YV1o.ToString() + "~" + Style.YV2o.ToString());
            DebugMsg.AppendLine("======================");

            DebugMsg.AppendLine("UserZoom=" + UserZoom.ToString());

            // newstr = "CHinfo=" + chinfo.GetUpperBound(0);

            foreach (CChinfo ch in chinfo)
            {
                DebugMsg.AppendLine("idx=" + ch.Idx.ToString() + ",visible:" + ch.Show.ToString() + ",cnt=" + ch.Value.Count.ToString() + ",Color=" + ch.Color.ToString());
            }

            fontsize = g.MeasureString(DebugMsg.ToString(), this.Font);
            g.FillRectangle(new SolidBrush(Color.FromArgb(180, Color.Black)), new Rectangle(100, 100, (int)(fontsize.Width * 1.3), (int)(fontsize.Height * 1.3)));
            g.DrawString(DebugMsg.ToString(), this.Font, Brushes.Tomato, 110, 110);
        }

        public List<String> Files = new List<string>();

        public void DataClear()
        {
            if (uc != null) uc.Clear();
            if (chinfo != null) Array.Clear(chinfo, 0, chinfo.Length);
            if (Time != null) this.Time.Clear();
            if (Files != null) Files.Clear();
        }

        /// <summary>
        /// 지정된 시간값이 현재 존재하는가?
        /// </summary>
        /// <param name="value">int64 : time value</param>
        /// <returns></returns>
        public Boolean ExistTime(Int64 _value)
        {
            //   if (Time.Count < 1) return false;

            return Time.Select(t => t.Value).ToList().Contains(_value);
            //for (int i = 1; i <= Time.Count; i++)
            //{
            //    if (_value == Time[i - 1].Value) return true;
            //}
            //return false;
        }

        public void ScaleFitX()
        {
            var xrange = this.RangeX;
            this.Style.XV1 = xrange.min;
            this.Style.XV2 = xrange.max;
            this.Invalidate();
        }
        public void ScaleFitY()
        {
            var xrange = this.RangeY;
            this.Style.YV1 = xrange.min;
            this.Style.YV2 = xrange.max;
            this.Invalidate();
        }
        public void ScaleFitXY()
        {
            ScaleFitX();
            ScaleFitY();
        }


        public void Draw_channel(Graphics g, RectangleF crect, RectangleF wrect)
        {
            //지정된 시간에 속한 타임만 취듯한다.
            var rangeX = this.RangeX;
            var minTime = this.Time[rangeX.min];
            var maxTime = this.Time[rangeX.max];

            var qry = Time
    .Where(t => t.Value >= Style.XV1 && t.Value <= Style.XV2)
    .OrderBy(t => t.Value)
    .ToDictionary(t => t.Key, t => t.Value);



            if (qry.Any() == false) return;

            //지정된 시간영역의 자료의 범위를 측정
            var time_min = qry.Min(t => t.Key); //최소시간정보
            var time_max = qry.Max(t => t.Key); //최대시간정보

            //각채널의 데이터를 확인한다(시간정보[최소~최대]에 포함된것만 표시한다)
            var chlist = chinfo.Where(t => t != null && t.Show
            && (t.Value.Max(s => s.Key) >= time_min && t.Value.Min(s => s.Key) <= time_max)).ToList();

            foreach (var ch in chinfo)
            {
                if (ch == null || ch.Show == false) continue;
                //var minX = ch.Value.Min(s => s.Key);
                //var maxX = ch.Value.Max(s => s.Key);

                //해당채널내에서 지정된 영역의 자료만 가져온다.
                var valueList = ch.Value.Where(t => t.Key >= time_min && t.Key <= time_max).ToDictionary(t => t.Key, t => t.Value);
                if (valueList.Any() == false) continue;

                List<PointF> linearrya = new List<PointF>(0);

                Single PreX = 0;
                Single PreY = 0;
                Boolean fc1 = false, fc2 = false;
                int idx = 0;

                //저장된 모든 값을 출력한다.
                foreach (var cdata in valueList)
                {
                    //시간정보는 동일하다면 조회하지 않게한다.
                    var time = qry[cdata.Key];
                    var volt = cdata.Value;

                    //좌표계산
                    Single newx = GetXfromTime(time, crect) - 1;
                    Single newy = GetYfromVolt(volt, crect) - 1;

                    //커서값확인
                    foreach (CUserCursor cs in uc)
                    {
                        if (cs.Idx == 0 && time >= cs.Time && fc1 == false)
                        {
                            if (volt > 60) ch.C1value = "OVER";
                            else ch.C1value = volt.ToString();
                            fc1 = true;
                        }
                        else if (cs.Idx == 1 && time >= cs.Time && fc2 == false)
                        {
                            if (volt > 60) ch.C2value = "OVER";
                            else ch.C2value = volt.ToString();
                            fc2 = true;
                        }
                    }

                    //너무가까운데이터는 그리지 않는다.
                    //if (Math.Abs(newx - PreX) < 1 && Math.Abs(newy - PreY) < 1)
                    //{
                    //    //Console.WriteLine("SKip Pixel");
                    //    continue;
                    //}
                    //else
                    //{
                    PreX = newx;
                    PreY = newy;
                    //}

                    if (newx >= wrect.Left && newx <= wrect.Left + wrect.Width)  //y축은 구분없이 모두 그리게한다.
                        linearrya.Add(new PointF(newx, newy));

                    if (Style.Show_DataPoint)
                    {
                        var size = 2;
                        g.FillEllipse(new SolidBrush(Color.FromArgb(180, ch.Color)), new RectangleF(newx - size, newy - size, size * 2, size * 2));
                        g.DrawEllipse(Pens.Black, new RectangleF(newx - size, newy - size, size * 2, size * 2));
                    }


                    idx += 1;

                }
                if (linearrya.Count > 1)
                    g.DrawLines(new Pen(ch.Color, 10), linearrya.ToArray());
            }
        }

        public void Draw_ZoomSelect(Graphics g)
        {
            //사용자줌영역표시
            if (this.UserZoom.Width != 0)
            {
                int x, y, w, h;

                w = Math.Abs(UserZoom.Width);
                h = Math.Abs(UserZoom.Height);

                if (UserZoom.Width > 0) x = UserZoom.Left;
                else x = UserZoom.Width + UserZoom.Left;

                if (UserZoom.Height > 0) y = UserZoom.Top;
                else y = UserZoom.Height + UserZoom.Top;

                //Y축 줌을 사용하지 않으므로 높이부분은 모두로 처리한다.
                if (!Style.UseZoomY)
                {
                    y = (int)ChartRect.Top;
                    h = (int)ChartRect.Height;
                }

                Rectangle rect = new Rectangle(x, y, w, h);

                Int64 startx = GetTimeFromX(rect.Left - this.ChartRect.Left, ChartRect);    //줌영역의 시작시간정보
                Int64 endx = GetTimeFromX(rect.Left + rect.Width - ChartRect.Left, ChartRect);  //줌영역의 종료시간정보

                Single starty = (Single)GetVoltfromY(rect.Top, ChartRect);
                Single endy = (Single)GetVoltfromY(rect.Top + rect.Height, ChartRect);

                String ZommMsg = "";

                //선택된 영역 Drawing
                TimeSpan zoomts = DateTime.FromFileTime(startx) - DateTime.FromFileTime(endx);
                if (Math.Abs(zoomts.TotalSeconds) >= Style.MaxZoomX && Math.Abs(starty - endy) >= Style.MaxZoomY && rect.Width > 20) //150224 줌영역자체의 크기는 10px를 넘도록
                {
                    //시간은 최소1분 , 전압값은 최소 1v
                    g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Lime)), rect);
                    ZommMsg = Math.Abs(zoomts.TotalSeconds).ToString("N0") + Style.UnitX;
                }
                else
                {
                    g.FillRectangle(new SolidBrush(Color.FromArgb(150, Color.Red)), rect);

                    if (rect.Width <= 20)
                    {
                        ZommMsg = "줌 선택영역을 넓게 지정하세요";
                    }
                    else
                    {
                        if (Math.Abs(zoomts.TotalSeconds) < Style.MaxZoomX)
                        {
                            ZommMsg = "줌을 활성화 하려면 최소(" + Style.MaxZoomX.ToString() + Style.UnitX + "의 구간을 선택해야합니다";
                        }
                        else
                        {
                            ZommMsg = "줌을 활성화 하려면 최소(" + Style.MaxZoomY.ToString() + Style.UnitY + "의 구간을 선택해야합니다";
                        }
                    }

                }

                g.DrawRectangle(Pens.Black, rect);

                //시간정보 Display
                g.DrawString(GetTimeStr(startx, true, true) + "," + (Style.Y2 - starty).ToString("#0.00") + Style.UnitY, this.Font, Brushes.Black, rect.Left, rect.Top);
                String fontstr = GetTimeStr(endx, true, true) + "," + (Style.Y2 - endy).ToString("#0.00") + Style.UnitY;
                SizeF fontsize = g.MeasureString(fontstr, this.Font);
                g.DrawString(fontstr, this.Font, Brushes.Black, rect.Left - 5 + rect.Width - fontsize.Width, rect.Top + rect.Height - fontsize.Height);

                if (ZommMsg != "")
                    DrawString(g, ZommMsg, this.Font, Brushes.Black, new PointF(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2), EALIGN.CENTER, EALIGN.CENTER);

            }
        }

        /// <summary>
        /// 화면의 좌,우 측에 존재하는 이돈버튼(월을 움직이는 버튼이며 월 이동을위해서 이벤트를 발생한다)
        /// </summary>
        /// <param name="G"></param>
        //public void Draw_NaviLR(Graphics G, RectangleF crect)
        //{
        //    return;

        //    //LEFT NAVI
        //    Single x, y, xw = 30;
        //    x = crect.Left - 3;
        //    y = crect.Top + crect.Height / 2 - xw / 2;

        //    PointF[] aa = new PointF[4];
        //    aa[0] = new PointF(x, y);
        //    aa[1] = new PointF(x - xw, y + xw);
        //    aa[2] = new PointF(x, y + xw + xw);
        //    aa[3] = new PointF(x, y);

        //    PointF[] aa1 = new PointF[4];
        //    aa1[0] = new PointF(x + 2, y + 2);
        //    aa1[1] = new PointF(x + 2 - xw, y + xw + 2);
        //    aa1[2] = new PointF(x + 2, y + xw + xw + 2);
        //    aa1[3] = new PointF(x + 2, y + 2);
        //    G.FillPolygon(Brushes.Black, aa1);
        //    G.FillPolygon(Brushes.DarkRed, aa);
        //    G.DrawPolygon(Pens.Black, aa);

        //    Buttons.Add(new RectangleF(x - xw, y, xw, xw*2));
        //    Buttonstag.Add("LBUTTON");
        //    Buttonstype.Add(EBUTTONTYPE.LEFTMON);

        //    //RIGHT NAVI
        //    x = crect.Left + crect.Width + 3;
        //    y = crect.Top + crect.Height / 2 - xw / 2;

        //    aa = new PointF[4];
        //    aa[0] = new PointF(x, y);
        //    aa[1] = new PointF(x + xw, y + xw);
        //    aa[2] = new PointF(x, y + xw + xw);
        //    aa[3] = new PointF(x, y);

        //    aa1 = new PointF[4];
        //    aa1[0] = new PointF(x + 2, y + 2);
        //    aa1[1] = new PointF(x + 2 + xw, y + xw + 2);
        //    aa1[2] = new PointF(x + 2, y + xw + xw + 2);
        //    aa1[3] = new PointF(x + 2, y + 2);
        //    G.FillPolygon(Brushes.Black, aa1);
        //    G.FillPolygon(Brushes.DarkRed, aa);
        //    G.DrawPolygon(Pens.Black, aa);
        //    Buttons.Add(new RectangleF(x, y, xw, xw*2));
        //    Buttonstag.Add("RBUTTON");
        //    Buttonstype.Add(EBUTTONTYPE.RIGHTMON);
        //}


        public String GetDateTimeStr(Int64 Filetime, Boolean linefeed)
        {
            DateTime TimeDate = DateTime.FromFileTime(Filetime);
            String timestr = "";
            if (linefeed) timestr = TimeDate.ToShortDateString() + "\n" + TimeDate.ToShortTimeString();  //' yymm + "-" + day.ToString("00") + "\n" + hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00");
            else timestr = TimeDate.ToString();// yymm + "-" + day.ToString("00") + " " + hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00");

            return timestr;
        }

        public String GetTimeStr(Int64 Filetime, Boolean withday, Boolean withseconed, Boolean nolf = false, Boolean showyear = false)
        {
            if (Filetime < 0D) return "-Err-";

            DateTime TimeDate = DateTime.FromFileTime(Filetime);
            String timestr = "";

            if (TimeDate.Second == 0 || !withseconed)
                timestr = TimeDate.ToString("HH:mm");//  TimeDate.Hour.ToString("00") + ":" +  TimeDate.Minute.ToString("00");
            else
                timestr = TimeDate.ToString("HH:mm:ss"); // hour.ToString("00") + ":" + min.ToString("00") + ":" + sec.ToString("00");

            if (withday)
            {
                if (showyear)
                {
                    timestr = TimeDate.ToString("yy-MM-dd") + " " + timestr;
                }
                else
                {
                    if (nolf) timestr = TimeDate.ToString("dd") + "일 " + timestr;
                    else timestr = timestr + "\n" + TimeDate.ToString("dd") + "일";
                }
            }
            return timestr;
        }

        /// <summary>
        /// (전체시간 * 현재X) / 전체너비
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public Int64 GetTimeFromX(Single X, RectangleF crect)
        {
            if (crect.Width < 1 || crect.Height < 1) return 0;
            DateTime sd = DateTime.FromFileTime(Style.XV1);
            DateTime ed = DateTime.FromFileTime(Style.XV2);
            TimeSpan ts = ed - sd;

            Int64 gap = (Int64)((ts.TotalMilliseconds * X) / crect.Width);
            return sd.AddMilliseconds(gap).ToFileTime();
        }
        public Single GetXfromTime(Int64 t)
        {
            return (((this.ChartRect.Width) * (t - Style.XV1)) / (Style.XV2 - Style.XV1)) + ChartRect.Left;
        }
        public Single GetXfromTime(Int64 t, RectangleF crect)
        {
            return (((crect.Width) * (t - Style.XV1)) / (Style.XV2 - Style.XV1)) + crect.Left;
        }
        public Single GetVoltfromY(Single Y, RectangleF crect)
        {

            return ((Style.Y2 - Style.Y1) * Y) / crect.Height;

        }
        public Single GetYfromVolt(Single volt, RectangleF crect)
        {
            //volt = (Style._endvolt - Style._startvolt) - volt;  150223 : Y값도 X축처럼 표시변수 YV 값으로 수정함
            return (crect.Height - ((crect.Height * (volt - Style.YV1)) / (Style.YV2 - Style.YV1))) + crect.Top;
        }

        private void DrawString(Graphics G, String str, Font font, Brush br, PointF ops, EALIGN XCenter = EALIGN.LEFT, EALIGN YCenter = EALIGN.LEFT)
        {
            SizeF fsize = G.MeasureString(str, font);
            Single newx = ops.X;
            Single newy = ops.Y;

            if (XCenter == EALIGN.CENTER) newx = ops.X - fsize.Width / 2;
            else if (XCenter == EALIGN.RIGHT) newx = ops.X - fsize.Width;

            if (YCenter == EALIGN.CENTER) newy = ops.Y - fsize.Height / 2;
            else if (YCenter == EALIGN.RIGHT) newy = ops.Y - fsize.Height;

            G.DrawString(str, font, br, newx, newy);
        }

        public Boolean INIT
        {
            get { return this.init; }
            set { this.init = value; }
        }
        public CChinfo[] CHInfo
        {
            get { return this.chinfo; }
            set { this.chinfo = value; }
        }

        /// <summary>
        /// 데이터가 추가되었거나 변경되었다면 이 값이 True가 되며 False는 manual 로 전환해야 합니다
        /// </summary>
        bool RangeUpdateData = true;
        uint pre_minX, pre_maxX;
        float pre_minY, pre_maxY;

        public (uint min, uint max) RangeX
        {
            get
            {

                return (pre_minX, pre_maxX);
            }
        }
        public (float min, float max) RangeY
        {
            get
            {

                return (pre_minY, pre_maxY);
            }
        }


        public int LegendWidth { get; set; } = 200;
        public RectangleF LegendRect { get; private set; }

        public Rectangle ScreenRect { get; private set; } = Rectangle.Empty;
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (this.Visible == false) return;
            Console.WriteLine("OnSizeChanged");


        }

        void CreateBufferGraphic()
        {
            if (this.bufferedGraphics != null)
            {
                bufferedGraphics.Dispose();
                bufferedGraphics = context.Allocate(this.CreateGraphics(), this.ClientRectangle);
                bufferedGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            }

        }
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            Console.WriteLine("OnResize");
            if (this.Visible == false) return;

            //크기변경이 없다면 처리하지 않는다.
            //if (ScreenRect.Equals(this.ClientRectangle))
            {
                if (this.bufferedGraphics == null && context != null)
                {
                    if (this.ClientRectangle.Width != 0 && this.ClientRectangle.Height != 0)
                    {
                        bufferedGraphics = context.Allocate(this.CreateGraphics(), this.ClientRectangle);
                        bufferedGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    }
                }
                return;
            }
            //ScreenRect = ClientRectangle;

            if (this.bufferedGraphics != null && context != null)
            {
                bufferedGraphics.Dispose();
                if (this.ClientRectangle.Width == 0 || this.ClientRectangle.Height == 0)
                {
                    bufferedGraphics = null;
                }
                else
                {
                    bufferedGraphics = context.Allocate(this.CreateGraphics(), this.ClientRectangle);
                    bufferedGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                }
            }


            LegendWidth = 100;
            this.WindowRect = new RectangleF(0, 0, this.Width, this.Height);
            if (ShowLegend)
            {
                this.ChartRect = new RectangleF(Padding.Left, Padding.Top,
            this.Width - (Padding.Left + 10) - LegendWidth - 20,
            (Single)(this.Height - (Padding.Top + Padding.Bottom)));
                LegendRect = new RectangleF(ChartRect.Right + 10, ChartRect.Y, LegendWidth + 10, ChartRect.Height);
            }
            else
            {
                this.ChartRect = new RectangleF(Padding.Left, Padding.Top,
                this.Width - (Padding.Left + Padding.Right) - 20,
                (Single)(this.Height - (Padding.Top + Padding.Bottom)));
                LegendRect = RectangleF.Empty;  // new RectangleF(ChartRect.Right + 20, ChartRect.Y, LegendWidth, ChartRect.Height);

            }

            int SW = this.Width;
            int SH = this.Height;
            if (this.Width == 0 || this.Height == 0)
            {
                SW = 600;
                SH = 600;
            }


            layer_bak = new Bitmap(SW, SH);
            layer_graph = new Bitmap(SW, SH);
            layer_bakT = new Bitmap(SW, SH);
            layer_graphT = new Bitmap(SW, SH);

            Graphics.FromImage(layer_bak).Clear(Color.White);
            Graphics.FromImage(layer_bakT).Clear(Color.White);

            Set_Refresh();
            Invalidate();
        }

        private int check_userpoint(MouseEventArgs e)
        {
            if (this.uc.Count == 0) return -1;
            for (int i = 0; i < this.uc.Count; i++)
            {
                CUserCursor uuc = this.uc[i];

                //현재위치가 해당 Rectagle 안이라면 속해있다.                    
                if ((int)this.Mouseinfo.Position.X >= (int)uuc.Newx - 2 && (int)this.Mouseinfo.Position.X <= (int)(uuc.Newx) + 2 && this.Mouseinfo.Position.Y <= ChartRect.Top)
                    return i;
            }
            return -1;
        }

        //private int check_buttons(MouseEventArgs e)
        //{
        //    if (this.Buttons == null) return -1;
        //    for (int i = 0; i < this.Buttons.Count; i++)
        //    {
        //        RectangleF rect = this.Buttons[i];

        //        //현재위치가 해당 Rectagle 안이라면 속해있다.                    
        //        if (e.X > rect.X && e.X < rect.X + rect.Width)
        //        {
        //            if (e.Y > rect.Y && e.Y < rect.Y + rect.Height) return i;
        //        }
        //    }
        //    return -1;
        //}

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // TrendCtrl
            // 
            this.Name = "TrendCtrl";
            this.Size = new System.Drawing.Size(287, 321);
            this.Load += new System.EventHandler(this.DispCtrl_Load);
            this.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseDoubleClick);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseDown);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseMove);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseUp);
            this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.TrendCtrl_MouseWhell);
            this.ResumeLayout(false);
        }

        public void Graph_Reset()
        {
            Style.ResetXxis();
            Style.ResetYxis();
            Set_Refresh();
            Console.WriteLine("Graph : Graph_Reset");
        }

        private void TrendCtrl_MouseDown(object sender, MouseEventArgs e)
        {


            switch (e.Button)
            {
                case System.Windows.Forms.MouseButtons.Left:

                    //드래그판단을위한 위치값을 저장한다.
                    Mouseinfo.DragStart = new PointF(e.X - this.WindowRect.Left, e.Y - WindowRect.Top);

                    //화면이동모드일때는 좌측버튼도 중앙버튼처럼 처리해야한다.
                    if (ScreenMove)
                    {
                        //X축 줌을 사용한경우에만 가능하게 한다.
                        if (Style.UseZoomX)
                        {
                            //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다.
                            if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height)
                            {
                                if (Style.XV2 == Style.X2 && Style.XV1 == Style.X1)
                                {
                                    //데이터영역과 표시영역이 동일하다(이동할 영역이 없는경우이다)
                                    Mouseinfo.DragType = EDRAGTYPE.NONE;
                                }
                                else
                                {
                                    //이동할영역이 존재하므로 이동시킨다.
                                    Mouseinfo.DragType = EDRAGTYPE.SCREEN;
                                    this.UserZoom = new Rectangle(0, 0, 0, 0);
                                }
                            }
                        }
                    }
                    else
                    {

                        //사용자포인트관련
                        int idx = check_userpoint(e);
                        if (idx != -1)
                        {
                            Mouseinfo.DragType = EDRAGTYPE.UC;
                            Mouseinfo.DragIndex = idx;
                            return;
                        }

                        //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다.
                        if (idx == -1 && e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height)
                        {
                            //좌측버튼은 항상 줌영역을 활성화한다
                            if (Style.UseZoomX)
                            {
                                Mouseinfo.DragType = EDRAGTYPE.ZOOM;
                                this.UserZoom = new Rectangle(e.X, e.Y, 0, 0);
                            }
                            else
                            {
                                //줌을 사용하지 않기로 하여 ㅆ다면?
                                Mouseinfo.DragType = EDRAGTYPE.NONE;
                            }
                        }
                    }

                    break;

                case System.Windows.Forms.MouseButtons.Middle:

                    //X축 줌을 사용한경우에만 가능하게 한다.
                    if (Style.UseZoomX)
                    {
                        //아무것도없는 빈공간이라면 화면이동(=줌시작)으로 대체한다.
                        if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width && e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height)
                        {
                            if (Style.XV2 == Style.X2 && Style.XV1 == Style.X1)
                            {
                                //데이터영역과 표시영역이 동일하다(이동할 영역이 없는경우이다)
                                Mouseinfo.DragType = EDRAGTYPE.NONE;
                            }
                            else
                            {
                                //이동할영역이 존재하므로 이동시킨다.
                                Mouseinfo.DragType = EDRAGTYPE.SCREEN;
                                this.UserZoom = new Rectangle(0, 0, 0, 0);
                            }
                        }
                    }

                    break;

                case System.Windows.Forms.MouseButtons.Right:   // 줌 초기화
                                                                // if (Message != null) Message("줌영역을 초기화합니다");
                    Style.XV1 = Style.XV1o;
                    Style.XV2 = Style.XV2o;
                    Style.Y1 = Style.YV1o;
                    Style.Y2 = Style.YV2o;
                    Set_Refresh(); //전체화면용 데이터를 바로 사용하면된다)

                    Console.WriteLine("Mouse R Click : REFRESH");
                    Refresh();
                    //if(Message != null) Message("");
                    break;

            }
        }

        private void TrendCtrl_MouseMove(object sender, MouseEventArgs e)
        {
            //마우스의 위치가 실시간 저장된다.
            if (this.Mouseinfo == null) this.Mouseinfo = new CMouseinfo(new PointF(e.X - this.WindowRect.Left, e.Y - this.WindowRect.Top));
            else this.Mouseinfo.Position = new PointF(e.X - this.WindowRect.Left, e.Y - this.WindowRect.Top);

            this.Mouseinfo.Time = GetTimeFromX(Mouseinfo.Position.X - ChartRect.Left, ChartRect); // +this.startview;
            this.Mouseinfo.Volt = this._Style.Y2 - GetVoltfromY(Mouseinfo.Position.Y - ChartRect.Top, ChartRect);
            this.Mouseinfo.Showinfo = false;

            //MouseStatus initialize...
            Mouseinfo.Hand = false;
            Mouseinfo.Cross = false;
            Mouseinfo.Move = false;
            Mouseinfo.ChangePos = false;

            //
            if (e.X >= ChartRect.X && e.X <= ChartRect.X + ChartRect.Width)
                if (e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height) Mouseinfo.Showinfo = true;
                else Mouseinfo.Showinfo = false;

            //이동중인데 마우스가 눌려있다면 DRAG다
            if (e.Button == MouseButtons.Left) Mouseinfo.Drag = true;
            else Mouseinfo.Drag = false;


            if (Mouseinfo.Drag) //드래그중이다
            {
                Mouseinfo.Move = true; //화면이동으로
            }
            else
            {
                if (e.X >= ChartRect.Left && e.X <= ChartRect.Left + ChartRect.Width)
                {
                    if (e.Y >= ChartRect.Top && e.Y <= ChartRect.Top + ChartRect.Height)
                    {
                        Mouseinfo.Cross = true; //화면내부라면 크로스
                    }
                    else Mouseinfo.Cross = false;
                }
                else Mouseinfo.Cross = false;
            }

            //사용자포인터위라면?
            int idx = this.check_userpoint(e);
            if (idx != -1)
            {
                Mouseinfo.Cross = false;
                Mouseinfo.Hand = false;
                Mouseinfo.Drag = false;
                Mouseinfo.ChangePos = true;
                Mouseinfo.DragType = EDRAGTYPE.UC;
            }

            //드래그중판단
            if (Mouseinfo.Drag)
            {
                switch (Mouseinfo.DragType)
                {
                    case EDRAGTYPE.NONE:
                        break;
                    case EDRAGTYPE.UC:
                        Single newx = (e.X);
                        Int64 newtime = GetTimeFromX(newx - ChartRect.Left, ChartRect);
                        if (Mouseinfo.DragIndex < uc.Count)
                        {
                            //데이터가 없는경우이다.
                            this.uc[Mouseinfo.DragIndex].Newx = newx;
                            this.uc[Mouseinfo.DragIndex].Time = newtime;
                        }
                        break;
                    case EDRAGTYPE.ZOOM:
                        this.UserZoom.Width = (int)(e.X - WindowRect.X - Mouseinfo.DragStart.X);
                        if (Key_Control || Style.UseZoomY)   //컨트롤이 눌려져있다면 줌을 사용한다.
                        {
                            this.UserZoom.Height = (int)(e.Y - WindowRect.Y - Mouseinfo.DragStart.Y);
                        }
                        else
                        {
                            this.UserZoom.Height = (int)ChartRect.Height;// (int)(e.Y - Mouseinfo.DragStart.Y - ChartRect.Left);
                        }

                        if (Mouseinfo.Cross) this.Cursor = Cursors.Cross;
                        break;
                    case EDRAGTYPE.SCREEN:

                        if (Mouseinfo.Move)
                        {
                            this.Cursor = Cursors.NoMove2D;
                            Mouseinfo.Showinfo = false;
                        }


                        //이동한pixcel을 시간정보로 변환한다.
                        //현재마우스위치값 - 드래그시작마우스위치값을 뺸 픽셀을 초로환산한다.
                        Single term = Mouseinfo.DragStart.X - Mouseinfo.Position.X; //양수라면 오른쪽으로 이동했다.
                        TimeSpan termtime = DateTime.FromFileTime(GetTimeFromX(Mouseinfo.Position.X, ChartRect)) - DateTime.FromFileTime(GetTimeFromX(Mouseinfo.DragStart.X, ChartRect));


                        Int64 NewSD = DateTime.FromFileTime(Style.XV1).AddSeconds(-1 * termtime.TotalSeconds).ToFileTime();// +  (int)mstime;
                        Int64 NewED = DateTime.FromFileTime(Style.XV2o).AddSeconds(-1 * termtime.TotalSeconds).ToFileTime(); //endviewo + (int)mstime;

                        //해당 시간영역의 전체 데이터 영역에 들어있어야 이동이 가능하다
                        if ((NewSD > Style.X1 && NewED < Style.X2))
                        {
                            Style.XV1 = NewSD;
                            Style.XV2 = NewED;

                            //y축 사용시에만 설정되게한다.
                            if (Key_Control || Style.UseZoomY)
                            {
                                Single termy = Mouseinfo.Position.Y - Mouseinfo.DragStart.Y - ChartRect.Top;
                                Single termvolt = GetVoltfromY(termy, ChartRect);
                                Single mvolt = termvolt;

                                Single SV = Style.YV1o + mvolt;
                                Single EV = Style.YV2o + mvolt;

                                Style.YV1 = SV;
                                Style.YV2 = EV;


                            }
                            Mouseinfo.DragStart = Mouseinfo.Position; //현재위치부터 드래그가 다시되도록
                            Set_Refresh();
                        }


                        break;
                }
            }
            else
            {
                if (Mouseinfo.Hand) this.Cursor = Cursors.Hand;
                if (Mouseinfo.Cross) this.Cursor = Cursors.Cross;
                if (Mouseinfo.ChangePos) this.Cursor = Cursors.SizeWE;
            }

            this.Refresh();
        }


        public void Graph_Left(int perc)
        {
            DateTime newStartview = DateTime.FromFileTime(this.Style.XV1);
            DateTime newEndview = DateTime.FromFileTime(this.Style.XV2);
            TimeSpan term = newEndview - newStartview;
            DateTime NewStart = newStartview.AddMinutes(-1 * (term.TotalMinutes / perc));
            DateTime NewEnd = newEndview.AddMinutes(-1 * (term.TotalMinutes / perc));
            if (NewStart < DateTime.FromFileTime(Style.X1)) return;
            this.Style.XV1 = NewStart.ToFileTime();
            this.Style.XV2 = NewEnd.ToFileTime();
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_Left");
        }

        public void Graph_Right(int perc)
        {
            DateTime newStartview = DateTime.FromFileTime(this.Style.XV1);
            DateTime newEndview = DateTime.FromFileTime(this.Style.XV2);
            TimeSpan term = newEndview - newStartview;
            DateTime NewStart = newStartview.AddMinutes(term.TotalMinutes / perc);
            DateTime NewEnd = newEndview.AddMinutes(term.TotalMinutes / perc);
            if (NewEnd > DateTime.FromFileTime(Style.X2)) return;
            this.Style.XV1 = NewStart.ToFileTime();
            this.Style.XV2 = NewEnd.ToFileTime();
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_Right");
        }

        /// <summary>
        /// 현재 그래프의 Y축 좌표영역을 10% 위로 이동합니다.
        /// </summary>
        public void Graph_Up()
        {
            Single v = (Style.YV2 - Style.YV1) / 10;
            Style.YV2 -= v;
            Style.YV1 -= v;
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_Up");
        }

        /// <summary>
        /// 현재 그래프의 Y축 좌표영역을 10% 아래로 이동합니다.
        /// </summary>
        public void Graph_Down()
        {
            Single v = (Style.YV2 - Style.YV1) / 10;
            Style.YV2 += v;
            Style.YV1 += v;
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_Down");
        }


        /// <summary>
        /// 현재 그래프의 Y축 좌표영역을 10% 확대합니다.
        /// </summary>
        public void Graph_ZoomInY()
        {
            Single v = (Style.YV2 - Style.YV1) / 10;
            Style.YV2 -= v;
            Style.YV1 += v;
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_ZoomInY");
        }

        /// <summary>
        /// 현재 그래프의 Y축 좌표영역을 10% 축소합니다.
        /// </summary>
        public void Graph_ZommOutY()
        {
            Single v = (Style.YV2 - Style.YV1) / 10;
            Style.YV2 += v;
            Style.YV1 -= v;
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : Graph_ZommOutY");
        }


        public void ZoomIN()
        {
            DateTime newStartview = DateTime.FromFileTime(this.Style.XV1);
            DateTime newEndview = DateTime.FromFileTime(this.Style.XV2);
            TimeSpan term = newEndview - newStartview;
            DateTime NewStart = newStartview.AddMinutes(term.TotalMinutes / 20);
            DateTime NewEnd = newEndview.AddMinutes(-1 * (term.TotalMinutes / 20));
            if (NewStart >= DateTime.FromFileTime(Style.X2).AddMinutes(-10)) NewStart = DateTime.FromFileTime(Style.X2).AddMinutes(-10);
            if (NewEnd <= DateTime.FromFileTime(Style.X1).AddMinutes(10)) NewEnd = DateTime.FromFileTime(Style.X1).AddMinutes(10);
            Style.XV1 = NewStart.ToFileTime();
            this.Style.XV2 = NewEnd.ToFileTime();
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : ZoomIN");
        }

        public void ZoomOUT()
        {
            DateTime newStartview = DateTime.FromFileTime(Style.XV1);
            DateTime newEndview = DateTime.FromFileTime(Style.XV2);
            TimeSpan term = newEndview - newStartview;
            DateTime NewStart = newStartview.AddMinutes(-1 * (term.TotalMinutes / 20));
            DateTime NewEnd = newEndview.AddMinutes(term.TotalMinutes / 20);
            if (newStartview < DateTime.FromFileTime(Style.X1)) NewStart = DateTime.FromFileTime(Style.X1);
            if (newEndview > DateTime.FromFileTime(Style.X2)) NewEnd = DateTime.FromFileTime(Style.X2);
            Style.XV1 = NewStart.ToFileTime();
            Style.XV2 = NewEnd.ToFileTime();
            Set_Refresh();
            Refresh();
            Console.WriteLine("Graph : ZoomOUT");
        }

        private void TrendCtrl_MouseWhell(object sender, MouseEventArgs e)
        {
            if (Key_Control)
            {
                //온도축 변경(Y)
                Single news, newe;

                news = Style.Y2;
                newe = Style.Y1;

                Single TimerTerm = newe - news;

                if (e.Delta > 0)
                {
                    //확대                   
                    news = news + (TimerTerm / 10);
                    newe = newe + (-1 * (TimerTerm / 10));
                }
                else
                {
                    //축소(down)
                    news = news + (-1 * (TimerTerm / 10));
                    newe = newe + (1 * (TimerTerm / 10));
                }

                if (news > 400) news = 400;
                if (news < -200) news = -200;


                Style.Y2 = news;
                Style.Y1 = newe;
            }
            else
            {
                //시간축 변경(X)
                DateTime news, newe;

                news = DateTime.FromFileTime(Style.XV1);
                newe = DateTime.FromFileTime(Style.XV2);

                TimeSpan TimerTerm = newe - news;

                if (e.Delta > 0)
                {
                    //확대
                    if (TimerTerm.TotalSeconds < Style.MaxZoomX) return;
                    news = news.AddSeconds(TimerTerm.TotalSeconds / 10);
                    newe = newe.AddSeconds(-1 * (TimerTerm.TotalSeconds / 10));
                }
                else
                {
                    //축소(down)
                    news = news.AddSeconds(-1 * (TimerTerm.TotalSeconds / 10));
                    newe = newe.AddSeconds(1 * (TimerTerm.TotalSeconds / 10));
                }

                if (news > DateTime.FromFileTime(Style.X2)) news = DateTime.FromFileTime(Style.X2);
                if (news < DateTime.FromFileTime(Style.X1)) news = DateTime.FromFileTime(Style.X1);
                if (newe > DateTime.FromFileTime(Style.X2)) newe = DateTime.FromFileTime(Style.X2);
                if (newe < DateTime.FromFileTime(Style.X1)) newe = DateTime.FromFileTime(Style.X1);

                if (news < DateTime.FromFileTime(Style.X1) || newe > DateTime.FromFileTime(Style.X2))
                {
                    //MessageBox.Show("데이터가 없으므로 줌을 사용할 수 없습니다");                
                }
                else
                {
                    Style.XV1 = news.ToFileTime();
                    Style.XV2 = newe.ToFileTime();
                    Style.XV1 = news.ToFileTime();
                    Style.XV2o = newe.ToFileTime();
                }

            }

            this.Set_Refresh();
            this.Refresh();
            Console.WriteLine("Graph : Mouse Wheel");
        }

        private void TrendCtrl_MouseUp(object sender, MouseEventArgs e)
        {
            Mouseinfo.Drag = false;
            if (Mouseinfo.DragType == EDRAGTYPE.UC)
            {
                Refresh_Grpah = true;   //다시값을 계산하려면 실행해야함
                this.Refresh();
                Console.WriteLine("Graph : MouseUp1");

                if (OnUpdateUserCursor != null)
                    OnUpdateUserCursor(Mouseinfo.DragIndex);
            }
            else
            {
                if (UserZoom.Width != 0 && e.Button == System.Windows.Forms.MouseButtons.Left) //줌영역이 있다면 줌을 활성화한다.
                {
                    int x1, x2, y1, y2;

                    x1 = UserZoom.Left;
                    x2 = UserZoom.Left + UserZoom.Width;

                    if (!Style.UseZoomY)
                    {
                        y1 = (int)ChartRect.Top;
                        y2 = (int)ChartRect.Bottom;
                    }
                    else
                    {
                        y1 = UserZoom.Top;
                        y2 = UserZoom.Bottom;
                    }


                    Int64 startx = GetTimeFromX(x1 - ChartRect.Left, ChartRect);
                    Int64 endx = GetTimeFromX(x2 - ChartRect.Left, ChartRect);

                    Single endy = Style.Y2 - GetVoltfromY(y1 - ChartRect.Top, ChartRect);
                    Single starty = Style.Y2 - GetVoltfromY(y2 - ChartRect.Top, ChartRect);

                    if (endx < startx)  //교환
                    {
                        Int64 t = startx;
                        startx = endx;
                        endx = t;
                    }

                    if (endy < starty)  //교환
                    {
                        Single t = starty;
                        starty = endy;
                        endy = t;
                    }

                    TimeSpan tst = DateTime.FromFileTime(endx) - DateTime.FromFileTime(startx);
                    Single VoltTerm = endy - starty;
                    if (tst.TotalSeconds >= Style.MaxZoomX && VoltTerm >= Style.MaxZoomY && UserZoom.Width > 20) //보기영역이 5초는 넘어야 줌이 가능한다.
                    {
                        this.Style.XV1 = startx;
                        this.Style.XV2 = endx;

                        this.Style.YV1 = starty;// +this.Style.최소값;
                        this.Style.YV2 = endy;// +this.Style.최대값;

                        UserZoom.Width = 0;
                        Set_Refresh();
                        this.Refresh();
                        Console.WriteLine("Graph : MouseUp2");
                    }
                    else
                    {
                        UserZoom.Width = 0;
                        Set_Refresh();
                        this.Refresh();
                        Console.WriteLine("Graph : MouseUp3");
                    }
                }
            }
        }

        private void TrendCtrl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ////마우스더블클릭
            //int idx = check_userpoint(e);
            //if (idx != -1)
            //{
            //    //삭제한다.
            //    uc.Remove(uc[Mouseinfo.DragIndex]);
            //    if (uc.Count == 1)
            //    {
            //        uc[0].Idx = 0;
            //        foreach (CChinfo c in this.chinfo)
            //        {
            //            c.C2value = "";
            //        }
            //    }

            //    Refresh_Grpah = true;   //다시갱신해야 값이 계산됨
            //    this.Refresh();

            //    if(OnUpdateUserCursor != null)
            //        OnUpdateUserCursor(Mouseinfo.DragIndex);

            //    return;
            //}

            //if (e.Button == System.Windows.Forms.MouseButtons.Left)
            //{
            //    if (this.uc.Count < 2)
            //    {
            //        Single newx = GetXfromTime(Mouseinfo.Time, ChartRect);
            //        this.uc.Add(new CUserCursor((short)this.uc.Count, Mouseinfo.Time, newx));

            //        Refresh_Grpah = true;   //다시갱신해야 값이 계산됨
            //        this.Refresh();

            //        if (OnUpdateUserCursor != null)
            //            OnUpdateUserCursor(this.uc.Count);
            //    }

            //}

        }


        private void DispCtrl_Load(object sender, EventArgs e)
        {

        }



    }       //end class

}   //end namespace