
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel;
using System.Web;
//using System.Security;

using Newtonsoft.Json;

namespace Legalsoft.Workflow.Application
{
    public static class Baby
    {
        public static string Company = "BEIJING LEGALSOFT LTD.";
        public static string Software = "FLOWSTUDIO 2017";
        public static string Release = "0.8.8.8";
        public static string EncoderName = "utf-8";
        public static string LocalUrl = "http://" + WebHelper.HttpHost + "/api/v1/";
        public static string RemoteUrl = "";
        public static string DataPath = WebHelper.Server.MapPath("/app_resource/");
        public static string U(string v)
        {
            return v.Replace("\"", "&#34;").Replace("'", "&#39;").Replace("<", "&lt;").Replace(">", "&gt;").Trim().ToLower();
        }
        public static string Summary()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<span class=\"information\" id=\"api_info\">");
            sb.AppendLine("Charactor Encoder Name = " + EncoderName + "<br>");
            sb.AppendLine("LocalUrl = " + LocalUrl + "<br>");
            sb.AppendLine("RemoteUrl = " + RemoteUrl + "<br>");
            sb.AppendLine("</span>");
            return sb.ToString();
        }
    }

    [Serializable]
    public class ParaInfo
    {
        //[DefaultValue("")]
        public string Name { set; get; }
        //[DefaultValue("")]
        public string Value { set; get; }
        public ParaInfo() { Name = Value = ""; }
    }

    [Serializable]
    public class CommandInfo
    {
        /// <summary>
        /// ID
        /// </summary>
        //[DefaultValue(0)]
        public int Id { set; get; }
        /// <summary>
        /// 流程名称（模板）
        /// </summary>
        //[DefaultValue("")]
        public string FlowName { set; get; }
        /// <summary>
        /// 流程实例ID
        /// </summary>
        //[DefaultValue(0)]
        public int FlowId { set; get; }
        /// <summary>
        /// 流程实例节点的ID
        /// </summary>
        //[DefaultValue(0)]
        public int NodeId { set; get; }
        //[DefaultValue("")]
        public string NodeName { set; get; }
        /// <summary>
        /// 用户角色名称
        /// </summary>
        //[DefaultValue("")]
        public string Role { set; get; }
        //[DefaultValue("")]
        public string LastRole { set; get; }
        /// <summary>
        /// 用户ID
        /// </summary>
        //[DefaultValue(0)]
        public int MemberId { set; get; }
        /// <summary>
        /// 用户NAME
        /// </summary>
        //[DefaultValue("")]
        public string MemberName { set; get; }
        /// <summary>
        /// 命令名称create,forward,backward,cancel
        /// </summary>
        //[DefaultValue("")]
        public string Operation { set; get; }
        /// <summary>
        /// 传送参数
        /// </summary>
        //[DefaultValue(null)]
        public List<ParaInfo> Paras { set; get; }
        /// <summary>
        /// 发送（来源）网址
        /// </summary>
        //[DefaultValue("")]
        public string LocalUrl { set; get; }
        /// <summary>
        /// 接收（出去）网址
        /// </summary>
        //[DefaultValue("")]
        public string RemoteUrl { set; get; }
        /// <summary>
        /// 时间戳
        /// </summary>
        //[DefaultValue("getdate()")]
        public string TimeStamp { set; get; }
        public CommandInfo()
        {
            Id = FlowId = NodeId = MemberId = 0;
            FlowName = NodeName = Role = LastRole = MemberName = Operation = LocalUrl = RemoteUrl = "";
            TimeStamp = DateTime.Now + "";
            Paras = null;
        }
    }

    public static class CryptHelper
    {
        public static string MD5(string inputString)
        {
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            string encoded = BitConverter.ToString(md5.ComputeHash(Encoding.Default.GetBytes(inputString))).Replace("-", "");
            return encoded.ToLower();
        }
    }

    public static class Commands
    {
        private static string _err = "";
        public static string Err() { string e = _err; _err = ""; return e; }
        private static bool CanSave = true;

        private static Queue Outbox = new Queue();
        public static void Send(CommandInfo cmd) { cmd.Id = Outbox.Count + 1; Outbox.Enqueue(cmd); }

        private static List<CommandInfo> Inbox = new List<CommandInfo>();
        public static void Receive(CommandInfo cmd) { cmd.Id = Inbox.Count + 1; Inbox.Add(cmd); }

        private static string ENCRYPT(string buf) { return buf; }
        private static string DECRYPT(string buf) { return buf; }

        public static string Page(HttpRequest Request)
        {
            if (Request["json"] == null) { return "form error:no json data"; }
            string memo = DECRYPT(Request["json"].ToString());
            if (Request["md5"] == null) { return "form error:no md5 data"; }
            string md5 = Request["md5"].ToString();
            if (CryptHelper.MD5(memo) != md5) { return "app error:invalid md5"; }
            try
            {
                CommandInfo cmd = JsonConvert.DeserializeObject<CommandInfo>(memo);
                if (Baby.RemoteUrl.Length < 1) Baby.RemoteUrl = cmd.LocalUrl;

                if (!cmd.Role.Contains("system"))
                {
                    Receive(cmd);
                    Save();
                }

                Tracer.Append("Receive from " + cmd.LocalUrl + " ok!");

                if (cmd.Role.Contains("system"))
                {
                    cmd.Operation = "forward";
                    Send(cmd);
                }

                return "ok";
            }
            catch (Exception ex)
            {
                return "[Commands.Page]" + ex.Message;
            }
        }

        public static CommandInfo Find(int id)
        {
            foreach (CommandInfo cx in Inbox)
            {
                if (cx.Id == id)
                {
                    return cx;
                }
            }
            return new CommandInfo();
        }

        private static bool RoleFitten(string last_role, string role_required, string role_member)
        {
            string[] ra_last = last_role.Split('.');
            string[] ra_req = role_required.Split('.');
            string[] ra_mbr = role_member.Split('.');
            int cnt = 0;
            for (int i = 0; i < 3; i++)
            {
                if (ra_req[i] == "any") cnt++;
                else if (ra_req[i] == "same" && ra_last[i] == ra_mbr[i]) cnt++;
                else if (ra_req[i] == ra_mbr[i]) cnt++;
            }
            return (cnt == 3) ? true : false;
        }

        /// <summary>
        /// 在用户提交之后，删除相应的Command
        /// </summary>
        public static void Dequeue(int id)
        {
            foreach (CommandInfo cx in Inbox)
            {
                if (cx.Id == id)
                {
                    Inbox.Remove(cx);
                    return;
                }
            }
        }

        public static void Save()
        {
            try
            {
                if (!CanSave) return;

                string memo = JsonConvert.SerializeObject(Outbox);
                File.WriteAllText(Baby.DataPath + "/outbox.json", memo, Encoding.GetEncoding("utf-8"));

                string meme = JsonConvert.SerializeObject(Inbox);
                File.WriteAllText(Baby.DataPath + "/inbox.json", meme, Encoding.GetEncoding("utf-8"));
            }
            catch (Exception ex)
            {
                CanSave = false;
                _err = "[Commands.Save]" + ex.Message;
                return;
            }
        }

        private static string UrlEncode(string inputData)
        {
            return HttpUtility.UrlEncode(inputData);
        }

        public static void Sending()
        {
            if (Outbox.Count < 1) return;
            CommandInfo cmd = (CommandInfo)Outbox.Dequeue();
            try
            {
                string json = JsonConvert.SerializeObject(cmd);
                string memo = ENCRYPT(json);
                string url = Baby.RemoteUrl + "?json=" + UrlEncode(memo) + "&md5=" + CryptHelper.MD5(json);
                _err = Networks.Download(url, Baby.EncoderName, 30);
                Tracer.Append("send to " + cmd.RemoteUrl + " " + _err);
            }
            catch (Exception ex)
            {
                Outbox.Enqueue(cmd);
                _err = "[Commands.Sending]" + ex.Message;
                Tracer.Append(_err);
            }
        }

        public static string Summary()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<span class=\"information\" id=\"commands_info\">");
            sb.AppendLine("There are " + Outbox.Count + " commands to send.<br>");
            if (Outbox.Count > 0)
            {
                sb.AppendLine("<table width=\"0%\" border=1>");
                sb.AppendLine("<tr style=\"background-color:#EEEEFF;\"><td colspan=2>Flow</td><td>Node</td><td colspan=2>Role</td><td>Member</td><td>Opr</td></tr>");
                foreach (CommandInfo cx in Outbox)
                {
                    sb.AppendLine("<tr>");
                    sb.AppendLine("<td>" + cx.FlowName + "</td>");
                    sb.AppendLine("<td>" + cx.FlowId + "</td>");
                    sb.AppendLine("<td>" + cx.NodeId + " " + cx.NodeName + "</td>");
                    sb.AppendLine("<td>" + cx.LastRole + "</td>");
                    sb.AppendLine("<td>" + cx.Role + "</td>");
                    sb.AppendLine("<td>" + cx.MemberId + " " + cx.MemberName + "</td>");
                    sb.AppendLine("<td>" + cx.Operation + "</td>");
                    sb.AppendLine("</tr>");
                }
                sb.AppendLine("</table>");
                sb.AppendLine("<br>");
            }

            sb.AppendLine("There are " + Inbox.Count + " commands have received.<br>");
            string last = "";
            if (Inbox.Count > 0)
            {
                sb.AppendLine("<table width=\"0%\" border=1>");
                sb.AppendLine("<tr style=\"background-color:#EEEEFF;\"><td colspan=2>Flow</td><td>Node</td><td colspan=2>Role</td><td>Member</td><td>Opr</td></tr>");
                foreach (CommandInfo cx in Inbox)
                {
                    if (last.Length == 0) { last = "<input type=\"hidden\" id=\"send_command_index\" value=\"" + cx.Id + "\">"; }
                    sb.AppendLine("<tr>");
                    sb.AppendLine("<td>" + cx.FlowName + "</td>");
                    sb.AppendLine("<td>" + cx.FlowId + "</td>");
                    sb.AppendLine("<td>" + cx.NodeId + " " + cx.NodeName + "</td>");
                    sb.AppendLine("<td>" + cx.LastRole + "</td>");
                    sb.AppendLine("<td>" + cx.Role + last + "</td>");
                    sb.AppendLine("<td>" + cx.MemberId + " " + cx.MemberName + "</td>");
                    sb.AppendLine("<td>" + cx.Operation + "</td>");
                    sb.AppendLine("</tr>");
                }
                sb.AppendLine("</table>");
            }
            sb.AppendLine("</span>");
            return sb.ToString();
        }
    }

    public static class Tracer
    {
        private static List<string> Items = new List<string>();
        public static void Append(string su) { Items.Add(DateTime.Now + " " + su); }
        public static string Summary()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<span class=\"information\" id=\"tracer_info\">");
            sb.AppendLine("History summary<br>");
            foreach (string su in Items)
            {
                string sk = su;
                while (sk.Length > 80)
                {
                    sb.AppendLine(sk.Substring(0, 80) + "<br>");
                    sk = sk.Substring(80);
                }
                sb.AppendLine(sk + "<br>");
            }
            sb.AppendLine("</span>");
            return sb.ToString();
        }
    }
}
