/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.IO;
using System.Dynamic;
using Microsoft.Scripting.Utils;
using System.Threading;

namespace Microsoft.Scripting.Hosting.Shell {

    public class BasicConsole : IConsole, IDisposable {

        private TextWriter _output;
        private TextWriter _errorOutput;
        private AutoResetEvent _ctrlCEvent;
        private Thread _creatingThread;

        public TextWriter Output {
            get { return _output; }
            set {
                ContractUtils.RequiresNotNull(value, "value");
                _output = value;
            }
        }

        public TextWriter ErrorOutput {
            get { return _errorOutput; }
            set {
                ContractUtils.RequiresNotNull(value, "value");
                _errorOutput = value;
            }
        }

        protected AutoResetEvent CtrlCEvent {
            get { return _ctrlCEvent; }
            set { _ctrlCEvent = value; }
        }

        protected Thread CreatingThread {
            get { return _creatingThread; }
            set { _creatingThread = value; }
        }

        private ConsoleColor _promptColor;
        private ConsoleColor _outColor;
        private ConsoleColor _errorColor;
        private ConsoleColor _warningColor;

        public BasicConsole(bool colorful) {            
            _output = System.Console.Out;
            _errorOutput = System.Console.Error;
            SetupColors(colorful);

            _creatingThread = Thread.CurrentThread;            

#if !SILVERLIGHT // ConsoleCancelEventHandler
            Console.CancelKeyPress += new ConsoleCancelEventHandler(delegate(object sender, ConsoleCancelEventArgs e) {
                if (e.SpecialKey == ConsoleSpecialKey.ControlC) {
                    e.Cancel = true;
                    _ctrlCEvent.Set();
                    _creatingThread.Abort(new KeyboardInterruptException(""));
                }
            });
#endif
            _ctrlCEvent = new AutoResetEvent(false);
        }

        private void SetupColors(bool colorful) {

            if (colorful) {
                _promptColor = PickColor(ConsoleColor.Gray, ConsoleColor.White);
                _outColor = PickColor(ConsoleColor.Green, ConsoleColor.White);
                _errorColor = PickColor(ConsoleColor.Red, ConsoleColor.White);
                _warningColor = PickColor(ConsoleColor.Yellow, ConsoleColor.White);
            } else {
#if !SILVERLIGHT
                _promptColor = _outColor = _errorColor = _warningColor = Console.ForegroundColor;
#endif
            }
        }

        private static ConsoleColor PickColor(ConsoleColor best, ConsoleColor other) {
#if SILVERLIGHT
            return best;
#else
            if (Console.BackgroundColor != best) {
                return best;
            }

            return other;
#endif
        }

        protected void WriteColor(TextWriter output, string str, ConsoleColor c) {
#if !SILVERLIGHT // Console.ForegroundColor
            ConsoleColor origColor = Console.ForegroundColor;
            Console.ForegroundColor = c;
#endif
            output.Write(str);
            output.Flush();

#if !SILVERLIGHT // Console.ForegroundColor
            Console.ForegroundColor = origColor;
#endif
        }

        #region IConsole Members

        public virtual string ReadLine(int autoIndentSize) {
            Write("".PadLeft(autoIndentSize), Style.Prompt);

            string res = Console.In.ReadLine();
            if (res == null) {
                // we have a race - the Ctrl-C event is delivered
                // after ReadLine returns.  We need to wait for a little
                // bit to see which one we got.  This will cause a slight
                // delay when shutting down the process via ctrl-z, but it's
                // not really perceptible.  In the ctrl-C case we will return
                // as soon as the event is signaled.
#if SILVERLIGHT
                if (_ctrlCEvent != null && _ctrlCEvent.WaitOne(100))
#else
                if (_ctrlCEvent != null && _ctrlCEvent.WaitOne(100, false))
#endif
 {
                    // received ctrl-C
                    return "";
                } else {
                    // received ctrl-Z
                    return null;
                }
            }
            return "".PadLeft(autoIndentSize) + res;
        }

        public virtual void Write(string text, Style style) {
            switch (style) {
                case Style.Prompt: WriteColor(_output, text, _promptColor); break;
                case Style.Out: WriteColor(_output, text, _outColor); break;
                case Style.Error: WriteColor(_errorOutput, text, _errorColor); break;
                case Style.Warning: WriteColor(_errorOutput, text, _warningColor); break;
            }
        }

        public void WriteLine(string text, Style style) {
            Write(text + Environment.NewLine, style);
        }

        public void WriteLine() {
            Write(Environment.NewLine, Style.Out);
        }

        #endregion

        #region IDisposable Members

        public void Dispose() {
            if (_ctrlCEvent != null) {
                _ctrlCEvent.Close();
            }

            GC.SuppressFinalize(this);
        }

        #endregion
    }

}

