using System ;
using System.Text ;
using System.IO ;
using System.Threading ;
using System.Drawing ;
using System.Collections.Generic ;
using System.Windows.Forms ;

using Manina.Windows.Forms ;
using log4net ;

// --------------------------------------------------------------------

public partial class MainForm : Form
{
    private readonly HashSet<string> mValidImageExtensions = new HashSet<string>{ ".png", ".jpg", ".gif" } ;

    private Dictionary<string,ChartImage> mChartImages = new Dictionary<string,ChartImage>() ;

    private SplitContainer mSplitter = new SplitContainer() ;
    private UserControl mSearchUserControl = new UserControl() ;
    private Label mSearchLabel = new Label() ;
    private TextBox mSearchQuery = new TextBox() ;
    private ImageListView mSearchResults = new ImageListView() ;
    private WebBrowser mStartupWebBrowser = new WebBrowser() ;
    private Panel mChartImagePanel = new Panel() ;
    private PictureBox mChartImagePictureBox = new PictureBox() ;

    private string mSearchResultsKey = null ;
    private Point? mMouseDragAnchor = null ;
    private Tuple<int,int> mScrollDragAnchor ;
    private double mUserZoom = 1.0 ;
    private DateTime mLastKeyPressTimeStamp = DateTime.Now ;

    public MainForm()
    {
        // initialize the form
        InitializeComponent() ;
    }

    private void loadChartImages()
    {
        // initialize
        ILog logger = LogManager.GetLogger( "startup" ) ;

        // build an index of the config entries
        HashSet<string> configIndex = new HashSet<string>(
            Program.dataConfig.data.ToObject< Dictionary<string,object> >().Keys
        ) ;

        // locate the chart images
        string dataDir = Path.GetFullPath( Program.dataDir ) ;
        IEnumerable<string> files = Directory.EnumerateFiles(
            dataDir, "*.*", SearchOption.AllDirectories
        ) ;
        foreach( string fname in files ) {

            // load the next image
            string extn = Path.GetExtension( fname ).ToLower() ;
            if ( ! mValidImageExtensions.Contains( extn ) )
                continue ;
            string key ;
            string fullPath = Path.GetFullPath( fname ) ;
            if ( fullPath.StartsWith( dataDir ) ) {
                key = fname.Substring( dataDir.Length ) ;
                key = key.Replace( "\\", "/" ) ;
                if ( key.StartsWith( "/" ) )
                    key = key.Substring( 1 ) ;
            } else {
                // NOTE: I don't think we should ever get here :-/ If we do, the user will have to manage
                // their configuration using full paths for the image files, but at least we will still run...
                key = fullPath ;
            }
            logger.Debug( $"Loading image: {key}" ) ;
            dynamic config = Program.dataConfig.data[ key ] ;
            if ( config == null )
                Program.logStartupMsg( "unconfigured-image", fname.Substring(dataDir.Length+1) ) ;
            else
                configIndex.Remove( key ) ;
            ChartImage chartImage = new ChartImage( key, fullPath, config ) ;
            mChartImages[ key ] = chartImage ;

            // add the image to the list
            ImageListViewItem item = chartImage.imageListViewItem ;
            mSearchResults.Invoke( (MethodInvoker) ( () => mSearchResults.Items.Add( item ) ) ) ;
        }

        // log any config entries that were unused
        foreach( string key in configIndex )
            Program.logStartupMsg( "unused-config", key ) ;

        // everything has been loaded, update the UI
        this.Invoke( (MethodInvoker) onStartupCompleted ) ;
    }

    private void onStartupCompleted()
    {
        // show any startup messages
        string unusedConfigs = Program.getStartupMsgs( "unused-config", "WARNING: Found unused configuration entries:" ) ;
        string unconfiguredImages = Program.getStartupMsgs( "unconfigured-image", "WARNING: Found image files with no configuration:" ) ;
        string badShortcuts = Program.getStartupMsgs( "bad-shortcut", "WARNING: Couldn't parse shortcuts:" ) ;
        string otherMsgs = Program.getStartupMsgs( "", "" ) ;
        string buf = "" ;
        string fname = Path.Combine( Program.resourcesDir, "startup-msgs.html" ) ;
        if ( File.Exists( fname ) ) {
            buf = File.ReadAllText( fname ) ;
            buf = buf.Replace( "{{UNUSED-CONFIGS}}", unusedConfigs ) ;
            buf = buf.Replace( "{{UNCONFIGURED-IMAGES}}", unconfiguredImages ) ;
            buf = buf.Replace( "{{BAD-SHORTCUTS}}", badShortcuts ) ;
            buf = buf.Replace( "{{OTHER-MESSAGES}}", otherMsgs ) ;
        }
        mStartupWebBrowser.DocumentText = buf ;

        // allow searches
        mSearchLabel.Enabled = true ;
        mSearchQuery.Enabled = true ;
        mSearchResults.Enabled = true ;
        mSearchQuery.Focus() ;
    }

    private void updateSearchResults( string searchQuery )
    {
        // initialize
        searchQuery = searchQuery.Trim() ;
        ILog logger = LogManager.GetLogger( "search" ) ;
        logger.Info( $"Updating search results: query=\"{searchQuery}\"" ) ;

        // search for matching chart images
        List< Tuple<ChartImage,float> > results = new List<Tuple<ChartImage,float>>() ;
        foreach( ChartImage chartImage in mChartImages.Values ) {
            float score ;
            if ( searchQuery == "" )
                score = 0 ;
            else {
                score = chartImage.getSearchScore( searchQuery ) ;
                if ( score <= 0 )
                    continue ;
            }
            results.Add( new Tuple<ChartImage,float>( chartImage, score ) ) ;
        }

        // sort the search results
        results.Sort( (lhs, rhs) => {
            if ( lhs.Item2 < rhs.Item2 )
                return +1 ;
            else if ( lhs.Item2 > rhs.Item2 )
                return -1 ;
            // NOTE: We also sort by caption/filename to handle things like "foo-1" vs. "foo-2".
            return String.Compare( lhs.Item1.caption(), rhs.Item1.caption() ) ;
        } ) ;
        if ( searchQuery != "" && logger.IsInfoEnabled ) {
            if ( results.Count > 0 ) {
                logger.Info( "- Sorted results:" ) ;
                foreach ( var val in results )
                    logger.Info( $"  - \"{val.Item1.caption()}\" = {val.Item2:F1}" ) ;
            } else
                logger.Info( "- No results." ) ;
        } else
            logger.Info( "- No search query, showing all chart images." ) ;

        // show the search results
        List<ChartImage> results2 = new List<ChartImage>() ;
        foreach ( var r in results )
            results2.Add( r.Item1 ) ;
        loadSearchResults( results2, searchQuery ) ;
    }

    private void loadSearchResults( IEnumerable<ChartImage> chartImages, string key )
    {
        // check if we need to reload the search results
        if ( key == mSearchResultsKey )
            return ;

        // clear the search results
        mSearchResults.SuspendLayout() ;
        mSearchResults.Items.Clear() ;
        mSearchResults.ResumeLayout( true ) ;

        // load the search results
        mSearchResults.SuspendLayout() ;
        int nItems = 0 ;
        foreach( ChartImage chartImage in chartImages ) {
            ImageListViewItem item = chartImage.imageListViewItem ;
            mSearchResults.Items.Add( item ) ;
            if ( nItems++ == 0 && key != "" )
                item.Selected = true ;
        }
        mSearchResults.ResumeLayout( true ) ;
        mSearchResultsKey = key ;
    }
}
