﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using Microsoft.Cci.Differs;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.Cci.Writers.Syntax
{
    public class HtmlSyntaxWriter : IndentionSyntaxWriter, IStyleSyntaxWriter, IReviewCommentWriter
    {
        private volatile bool _hasColor = false;

        private Action _writeColor;
        private Action _writeBackground;

        public HtmlSyntaxWriter(TextWriter writer)
            : base(writer)
        {
            WriteCore("<pre>");
        }

        public bool StrikeOutRemoved { get; set; }

        public IDisposable StartStyle(SyntaxStyle style, object context)
        {
            IDisposable disposeAction = null;
            switch (style)
            {
                case SyntaxStyle.Added:
                    disposeAction = WriteColor("green");
                    break;

                case SyntaxStyle.Removed:
                    string extraStyle = this.StrikeOutRemoved ? " text-decoration:line-through;" : "";
                    disposeAction = WriteColor("red", extraStyle);
                    break;

                case SyntaxStyle.InheritedMember:
                case SyntaxStyle.InterfaceMember:
                case SyntaxStyle.Comment:
                    disposeAction = WriteColor("gray");
                    break;
                case SyntaxStyle.NotCompatible:
                    disposeAction = WriteBackground("yellow", context);
                    break;

                default:
                    throw new NotSupportedException("Style not supported!");
            }

            Contract.Assert(disposeAction != null);
            return new DisposeAction(() => disposeAction.Dispose());
        }

        public void Write(string str)
        {
            WriteEncoded(str);
        }

        public void WriteSymbol(string symbol)
        {
            WriteEncoded(symbol);
        }

        public void WriteIdentifier(string id)
        {
            WriteEncoded(id);
        }

        public void WriteKeyword(string keyword)
        {
            using (WriteColor("blue"))
                WriteEncoded(keyword);
        }

        public void WriteTypeName(string typeName)
        {
            using (WriteColor("#2B91AF"))
                WriteEncoded(typeName);
        }

        public void WriteReviewComment(string author, string text)
        {
            var comment = "> " + author + ": " + text;
            using (WriteColor("gray", "font-weight: bold;padding-left: 20px;"))
                WriteEncoded(comment);
        }

        protected override void WriteCore(string s)
        {
            if (_writeColor != null)
            {
                // Need to not get into an infinte loop
                Action write = _writeColor;
                _writeColor = null;
                write();
            }

            if (_writeBackground != null)
            {
                Action write = _writeBackground;
                _writeBackground = null;
                write();
            }

            base.WriteCore(s);
        }

        public void Dispose()
        {
            WriteCore("</pre>");
        }

        private IDisposable WriteColor(string color, string extraStyle = "")
        {
            if (_writeColor != null || _hasColor)
                return new DisposeAction(() => { });

            _hasColor = true;
            _writeColor = () =>
                WriteCore(string.Format("<span style=\" color: {0};{1}\">", color, extraStyle));

            return new DisposeAction(() =>
            {
                _hasColor = false;
                // Only write if we wrote the begining tag
                if (_writeColor == null)
                    WriteCore("</span>");

                _writeColor = null;
            });
        }

        private IDisposable WriteBackground(string color, object context)
        {
            string tooltip = "";

            IEnumerable<IncompatibleDifference> differences = context as IEnumerable<IncompatibleDifference>;
            if (context != null)
            {
                tooltip = string.Join(", ", differences.Select(d => d.Message));
            }

            _writeBackground = () =>
                WriteCore(string.Format("<span style=\"background-color:{0}\" title=\"{1}\">", color, tooltip));

            return new DisposeAction(() =>
            {
                // Only write if we wrote the begining tag
                if (_writeBackground == null)
                    WriteCore("</span>");

                _writeBackground = null;
            });
        }

        private void WriteEncoded(string s)
        {
            WriteCore(WebUtility.HtmlEncode(s));
        }
    }
}
