/*
 * Common.cs
 * Copyright (c) 2007-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace LipSync {

    /// <summary>
    /// LipSync共用の関数
    /// </summary>
    public class Common {
        private static string m_log_file = "";
        private static StreamWriter m_sw = null;
        private static SaveFileDialog s_dialog = null;
        public static Color CURVE_X = Color.Black;
        public static Color CURVE_Y = Color.White;
        public static Color CURVE_SCALE = Color.Orange;
        public static Color CURVE_ALPHA = Color.Red;
        public static Color CURVE_ROTATE = Color.Navy;

        /// <summary>
        /// ファイルダイアログのFilterに指定しようとしている文字列がエラーを起こさないかどうかを確かめます
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static bool CheckFilterValidity( string filter ) {
            if ( s_dialog == null ) {
                s_dialog = new SaveFileDialog();
            }
            try {
                s_dialog.Filter = filter;
                return true;
            } catch {
                return false;
            }
        }

        public static void DebugWriteLine( string s ) {
#if DEBUG
#if MONO
            Console.WriteLine( s );
            System.Diagnostics.Trace.WriteLine( s );
            System.Diagnostics.Debug.WriteLine( s );
#else
            //System.Diagnostics.Debug.WriteLine( s );
            Console.WriteLine( s );
#endif
#endif
        }

        public static Image GetThumbnailImage( Image image, int width, int height ) {
            Bitmap res = new Bitmap( width, height, PixelFormat.Format32bppArgb );
            if ( image == null ) {
                return res;
            }
            int w = image.Width;
            int h = image.Height;
            float ASPECTO = (float)width / (float)height;
            float aspecto = (float)w / (float)h;
            float order = 1f; //拡大率
            int top = 0;      //描画位置y座標
            int left = 0;     //描画位置x座標
            if ( ASPECTO > aspecto ) {
                // サムネイルのほうが横長
                order = (float)height / (float)h;
                left = (int)(width - order * w) / 2;
            } else {
                // サムネイルのほうが縦長
                order = (float)width / (float)w;
                top = (int)(height - order * h) / 2;
            }
            using ( Graphics g = Graphics.FromImage( res ) ) {
                g.Clear( Color.Transparent );
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.DrawImage( image, left, top, w * order, h * order );                    
            }
            return res;
        }

        /// <summary>
        /// 2つの整数の最大公約数を返します。
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static long GetGCD( long m, long n ) {
            if ( n > m ) {
                long a = n;
                n = m;
                m = a;
            }
            while ( true ) {
                if ( n == 0 ) {
                    return m;
                }
                long quotient = m / n;
                long odd = m - n * quotient;
                if ( odd == 0 ) {
                    return n;
                }
                m = n;
                n = odd;
            }
        }
        

        public static Point PointFromPointF( PointF point_f ) {
            return new Point( (int)point_f.X, (int)point_f.Y );
        }


        public static Size SizeFromSizeF( SizeF size_f ) {
            return new Size( (int)size_f.Width, (int)size_f.Height );
        }


        /// <summary>
        /// 画像から不透明領域を検出する。
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Rectangle GetNonTransparentRegion( Bitmap bmp ) {
            if ( bmp.PixelFormat != PixelFormat.Format32bppArgb ) {
                return new Rectangle();
            }
            BitmapData bmpdat = bmp.LockBits(
                new Rectangle( 0, 0, bmp.Width, bmp.Height ),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            int stride = bmpdat.Stride;

            int ymax = 0;
            int ymin = (bmp.Height - 1) * stride;
            int xmax = 0;
            int xmin = (bmp.Width - 1) * 4;
            const byte ZERO = 0;
            unsafe {
                byte* dat = (byte*)(void*)bmpdat.Scan0;
                int xend = bmp.Width * 4;
                int yend;
                for ( int x = 0; x < xend; x += 4 ) {
                    // yminを決める
                    yend = ymin;//ymin* stride;
                    for ( int y = 0; y <= yend; y += stride ) {
                        if ( dat[x + y + 3] != ZERO ) {
                            //ymin = Math.Min( ymin, y / stride );
                            ymin = Math.Min( ymin, y );
                            break;
                        }
                    }

                    // ymaxを決める
                    yend = ymax;// ymax * stride;
                    for ( int y = (bmp.Height - 1) * stride; y >= yend; y -= stride ) {
                        if ( dat[x + y + 3] != ZERO ) {
                            //ymax = Math.Max( ymax, y / stride );
                            ymax = Math.Max( ymax, y );
                            break;
                        }
                    }
                }

                yend = ymax;// ymax * stride;
                for ( int y = ymin; y <= yend; y += stride ) {
                    // xminを決める
                    for ( int x = 0; x < xmin; x += 4 ) {
                        if ( dat[x + y + 3] != ZERO ) {
                            //xmin = Math.Min( xmin, x / 4 );
                            xmin = Math.Min( xmin, x );
                            break;
                        }
                    }

                    // xmaxを決める
                    for ( int x = (bmp.Width - 1) * 4; x >= xmax; x -= 4 ) {
                        if ( dat[x + y + 3] != ZERO ) {
                            //xmax = Math.Max( xmax, x / 4 );
                            xmax = Math.Max( xmax, x );
                            break;
                        }
                    }
                }
                if ( xmax <= xmin || ymax <= ymin ) {
                    xmin = 0;
                    xmax = bmp.Width - 1;
                    ymin = 0;
                    ymax = bmp.Height - 1;
                } else {
                    xmin = xmin / 4;
                    xmax = xmax / 4;
                    ymin = ymin / stride;
                    ymax = ymax / stride;
                }
            }
            bmp.UnlockBits( bmpdat );
            return new Rectangle( xmin, ymin, xmax - xmin + 1, ymax - ymin + 1 );
        }
                
        public static void LogPush( Exception ex ) {
            LogPush( ex.Source + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace );
        }
        public static void LogPush( string procedure, string message_type, string message ) {
            LogPush( procedure + ";" + message_type + ";" + message );
        }
        static void LogPush( string message ) {
            if ( m_sw == null ) {
                m_log_file = Path.Combine( Application.StartupPath, "error.log" );
                m_sw = new StreamWriter( m_log_file, true, Encoding.Unicode );
                m_sw.WriteLine( "************************************************************************" );
                m_sw.WriteLine( "Logger started : " + DateTime.Now.ToString() );
                m_sw.WriteLine( "------------------------------------------------------------------------" );
            }
            m_sw.WriteLine( DateTime.Now.ToString() + ";" + message );
            m_sw.Flush();
        }

        public static void LogClose() {
            if ( m_sw != null ) {
                m_sw.Close();
                m_sw = null;
            }
        }

        /// <summary>
        /// 指定したパスのファイルからイメージを読み込みます
        /// </summary>
        /// <param name="fpath">イメージファイルへのパス</param>
        /// <returns></returns>
        public static Image ImageFromFile( string fpath ) {
            Bitmap result = null;
            if ( File.Exists( fpath ) ) {
                using ( FileStream fs = new FileStream( fpath, FileMode.Open, FileAccess.Read ) ) {
                    Image temp = Image.FromStream( fs );
                    result = new Bitmap( temp.Width, temp.Height, PixelFormat.Format32bppArgb );
                    using ( Graphics g = Graphics.FromImage( result ) ) {
                        g.DrawImage( temp, 0, 0, temp.Width, temp.Height );
                    }
                    temp.Dispose();
                }
            }
            return result;
        }
    }

}
