using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using TCX.Configuration;
using System.IO;
using TCX.PBXAPI;
using System.Text;

namespace FuncsNS
{
    public class VMInfo : IComparable
    {
        public bool isRead;
        public string sCreated;
        public string wav_file;
        public string caller;
        public int dur;
        public string xml_file;

        #region IComparable Members
        public int CompareTo(object obj)
        {
            if (obj is VMInfo)
            {
                VMInfo inf = obj as VMInfo;
                if (!isRead && inf.isRead)
                    return -1;
                else if (isRead && !inf.isRead)
                    return 1;
                return -sCreated.CompareTo(inf.sCreated);
            }
            return 0;
        }
        #endregion

        public VMInfo(XmlDocument doc, string fname)
        {
            xml_file = fname;
            int flag = int.Parse(doc.SelectSingleNode("/VOICEMSG/FLAGS").InnerText);
            isRead = (flag & 1) == 1;
            sCreated = doc.SelectSingleNode("/VOICEMSG/CREATED").InnerText;
            XmlNode fileN = doc.SelectSingleNode("/VOICEMSG/FILE");
            wav_file = fileN.InnerText;
            caller = fileN.Attributes["CALLER"].Value;
            dur = int.Parse(fileN.Attributes["DURATION"].Value);
        }
    };

};

public partial class Funcs : System.Web.UI.Page
{
    //private static String vm_path = 
    //    @"C:\Program Files\3CX PhoneSystem\Data\Ivr\Voicemail\Extensions\";

    private void AddAudio(XmlNode blk, string p)
    {
        XmlNode aud = blk.OwnerDocument.CreateElement("audio");
        blk.AppendChild(aud);

        Utils.AddAttr(aud, "src", Utils.GetPrompt(p));
    }

    private void AddAudioFile(XmlNode blk, string p)
    {
        XmlNode aud = blk.OwnerDocument.CreateElement("audio");
        blk.AppendChild(aud);

        Utils.AddAttr(aud, "src", p);
    }

    protected XmlNode AddVar(XmlNode dest, string name, string expr)
    {
        XmlNode var = dest.OwnerDocument.CreateElement("var");
        dest.AppendChild(var);
        Utils.AddAttr(var, "name", name);
        Utils.AddAttr(var, "expr", expr);
        return var;
    }

    protected void GetParam(XmlNode form)
    {
        String param = Request.Params.Get("pname");
        String val = "0";
        String error_msg = "";

        if (!String.IsNullOrEmpty(param))
        {
            TCX.Configuration.Parameter pval = PhoneSystem.Root.GetParameterByName(param);
            if (pval != null)   val = pval.Value;
            else                error_msg = "PARAMNOTAVAILABLE";
        }
        else error_msg = "EMPTYPARAM";

        AddVar(form, "value", "'"+val+"'");
        AddVar(form, "error_msg", "'" + error_msg + "'");

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);
        XmlNode ret = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(ret);
        Utils.AddAttr(ret, "namelist", "value error_msg");
    }

    private void TransferToNumber(XmlNode form)
    {
        IVR dr = PhoneSystem.Root.GetDNByNumber(Request.Params.Get("dr")) as IVR;
        string destnumber = Request.Params.Get("dn");
        DN dest = PhoneSystem.Root.GetDNByNumber(destnumber);
        bool disabled = (dr == null || dest == null);
        DNProperty drp=null;
        DNProperty dnp=null;
        if(!disabled)
        {
            if(dest is SpecialMenu)
            {
                drp = dr.GetPropertyByName("DIRECT_SPECIALMENU");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            else if(dest is IVR)
            {
                drp = dr.GetPropertyByName("DIRECT_IVR");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            else if(dest is TCX.Configuration.Queue)
            {
                drp = dr.GetPropertyByName("DIRECT_QUEUE");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            else if(dest is RingGroup)
            {
                drp = dr.GetPropertyByName("DIRECT_RINGGROUP");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            else if((dest is ConferencePlaceExtension)&&(dest as ConferencePlaceExtension).IsGateway)
            {
                drp = dr.GetPropertyByName("DIRECT_CONFERENCE");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            else if(dest is Extension)
            {
                drp = dr.GetPropertyByName("DIRECT_EXTENSION");
                dnp = dest.GetPropertyByName("DIRECT_FROM_IVR");
            }
            disabled = ((drp!=null)&&(drp.Value=="0"))||((dnp!=null)&&(dnp.Value=="0"));
        }
	    else if(dr!=null)
        {
            TCX.Configuration.Parameter noheckfordn = PhoneSystem.Root.GetParameterByName("IVRNOCHECKFORDN");
            if (noheckfordn == null || noheckfordn.Value == "0")
            {
                //DNCheck is enforced redirect to the number specified by REDIRECT_UNKNOWN_TO property
                drp = dr.GetPropertyByName("REDIRECT_UNKNOWN_TO");
                if (drp != null && drp.Value != "")
                {
                    destnumber = drp.Value;
                    disabled = false;
                }
            }
            else
            {
                disabled = false;
                String tmp = null;
                //comma separated list of exact numbers or range of numbers
                noheckfordn = PhoneSystem.Root.GetParameterByName("IVRDONOTCALL");
                if (noheckfordn != null)
                {
                    tmp = noheckfordn.Value;
                }
                //comma separated list of exact numbers or range of numbers
                drp = dr.GetPropertyByName("DO_NOT_CALL");
                if (drp != null)
                {
                    if (tmp == null)
                    {
                        //set
                        tmp = drp.Value;
                    }
                    else
                    {
                        //join with global
                        tmp += ",";
                        tmp += drp.Value;
                    }
                }
                if (tmp != null && tmp != "")
                {
                    String[] blockednumbers = tmp.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (String t in blockednumbers)
                    {
                        if (t == destnumber)
                        {
                            //exact number don't allow
                            drp = dr.GetPropertyByName("REDIRECT_UNKNOWN_TO");
                            if (drp != null && drp.Value != "")
                            {
                                destnumber = drp.Value;
                            }
                            else
                            {
                                disabled = true;
                            }
                            break;
                        }
                        else if (t.IndexOf('-') != -1)
                        {
                            //range
                            try
                            {
                                String[] range = t.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
                                if (range.Length == 2 && (range[0].Length == range[1].Length))
                                {
                                    if ((destnumber.CompareTo(range[0]) >= 0) && (destnumber.CompareTo(range[1]) <= 0))
                                    {
                                        drp = dr.GetPropertyByName("REDIRECT_UNKNOWN_TO");
                                        if (drp != null && drp.Value != "")
                                        {
                                            destnumber = drp.Value;
                                        }
                                        else
                                        {
                                            disabled = true;
                                        }
                                        //in range. Do not allow call
                                        break;
                                    }
                                }
                            }
                            catch
                            {
                            }
                        }
                    }

                }
            }
        }

        AddVar(form, "disabled", disabled?"1":"0");
        AddVar(form, "destnumber", "'"+destnumber+"'");
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);
        XmlNode ret = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(ret);
        Utils.AddAttr(ret, "namelist", "disabled destnumber");
    }


    protected void CheckExt(XmlNode form)
    {
        String dn = Request.Params.Get("dn");
        bool vm_enabled = false, vm_playcid = false;
        int vm_playdt = 0;
        String pin = "";
        String error_msg = "";

        if (dn != null)
        {
            DN extDn = PhoneSystem.Root.GetDNByNumber(dn);
            if (extDn != null && extDn is Extension)
            {
                Extension ext = extDn as Extension;
                pin = ext.VMPIN;
                vm_enabled = ext.VMEnabled;
                vm_playcid = ext.VMPlayCallerID;
                vm_playdt = (int)ext.VMPlayMsgDateTime;
                if (!vm_enabled)
                    error_msg = "VMLDSBL";
            }
            else    error_msg = "EXTNOTFOUND";
        }
        else    error_msg = "EXTNOTFOUND";

        AddVar(form, "vm_enabled", vm_enabled ? "'1'" : "'0'");
        //AddVar(form, "pin", "'" + pin + "'");
        AddVar(form, "playcid", vm_playcid ? "'1'" : "'0'");
        AddVar(form, "playdt", "'" + vm_playdt.ToString() + "'");
        AddVar(form, "error_msg", "'" + error_msg + "'");

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        XmlNode ret = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(ret);

//      Utils.AddAttr(ret, "namelist", "vm_enabled pin playcid playdt error_msg");
        Utils.AddAttr(ret, "namelist", "vm_enabled playcid playdt error_msg");
    }

    protected void CheckPin(XmlNode form)
    {
        String dn = Request.Params.Get("dn");
        String pin = Request.Params.Get("pin");
        bool validpin = false, loginabuse=true;
        String error_msg = "";

        if (dn != null) 
            loginabuse = Utils.IsLoginAbused(dn);
        if ((dn != null) && (!loginabuse))
        {
            DN extDn = PhoneSystem.Root.GetDNByNumber(dn);
            if (extDn != null && extDn is Extension)
            {
                Extension ext = extDn as Extension;
                if (pin == ext.VMPIN)   validpin = true;
                else                    Utils.FailedLoginAttempt(dn);
            }
        }

        if (validpin)   AddVar(form, "validpin", "'1'");
        else            AddVar(form, "validpin", "'0'");
        if (loginabuse) AddVar(form, "abuse", "'1'");
        else            AddVar(form, "abuse", "'0'");

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        XmlNode ret = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(ret);

        Utils.AddAttr(ret, "namelist", "validpin abuse");
    }

    protected void EnterVM(XmlNode form)
    {
        String ext = Request.Params.Get("ext");
        AddVar(form, "session_id", "'" + Session.SessionID + "'");
        string vm_path = Utils.GetVoicemailPath() + @"\Extensions\";
        string[] files;
        try
        {
            files = System.IO.Directory.GetFiles(vm_path + ext, "*.xml");
        }
        catch (Exception ex)
        {
            files = new string[0];
        }
        int read = 0, unread = 0;
        System.Collections.SortedList list =
            new SortedList(files.Length);
        foreach (string f in files)
        {
            try
            {
                XmlDocument inf = new XmlDocument();
                inf.Load(f);
                FuncsNS.VMInfo vmi = new FuncsNS.VMInfo(inf, f);
                list.Add(vmi, vmi.isRead);
                if (vmi.isRead)
                    read++;
                else
                    unread++;
            }
            catch (Exception ex)
            {
            }
        }

        AddVar(form, "new_cnt", read.ToString());
        AddVar(form, "old_cnt", unread.ToString());

        String ret_str = "session_id new_cnt old_cnt";

        Session["VmailsList"] = list;
        Session["VM_pos"] = 0;

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        PromptSet ps = Utils.GetPromptSet();
        bool germ = ps.UseAlternateNumberPronunciation;

        if (unread > 0)
        {
            AddAudio(blk, "YAHV");
            SayNumber(blk, unread, germ);
            AddAudio(blk, unread == 1 ? "NEW_MSG" : "NEW_MSGS");
        }
        else
        {
            AddAudio(blk, "YAHVNO");
            AddAudio(blk, read == 0 ? "MSGS" : "NEW_MSGS");
        }
        if (read > 0)
        {
            AddAudio(blk, "AND");
            SayNumber(blk, read, germ);
            AddAudio(blk, "OLD");
            AddAudio(blk, read == 1 ? "MSG" : "MSGS");
        }

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
        Utils.AddAttr(retN, "namelist", ret_str);
    }

    private void PlayVM()
    {
        String fname = "x-path:";
        String ext = Request.Params.Get("ext");
        String act = Request.Params["act"];
        String fwdext = Request.Params["fwdext"];

        if (Session["VmailsList"] == null || Session["VM_pos"] == null)
        {
            fname += Utils.GetPrompt("OPFAIL");
        }
        else
        {
            SortedList list = Session["VmailsList"] as SortedList;
            int pos = (int)Session["VM_pos"];
            String prmpt = "";

            FuncsNS.VMInfo vm = GetVMIAtPos(list, ref pos, act, ref prmpt, ref ext, ref fwdext);
            string vm_path = Utils.GetVoicemailPath() + @"\Extensions\";
            if (vm != null)
            {
                fname += Path.Combine(vm_path + ext, vm.wav_file);
            }
            else
            {
                fname += Utils.GetPrompt(prmpt);
            }
            Session["VM_pos"] = pos;
        }

        Response.BufferOutput = true;
        Response.Expires = 0;
        Response.Cache.SetNoStore();
        byte[] data = Encoding.UTF8.GetBytes(fname);
        int length = data.Length;
        Response.ContentType = "text/plain";
        Response.AddHeader("Content-Length", length.ToString());
        Response.OutputStream.Write(data, 0, length);
//        Server.HtmlEncode(fname, Response.Output);
        Response.AddHeader("Connection", "close");
        Response.Flush();
    }

    private void SelfIdMsgVM()
    {
        String fname = "x-path:";
        String ext;
        try
        {
            ext = Request.Params.Get("ext");
            string base_path = Utils.GetVoicemailPath();
            base_path = Path.GetFullPath(Path.Combine(base_path, @".\Data\"));
            if (ext == null)
            {
                fname += Utils.GetPrompt("EMPTY");
            }
            else
            {
                String path = Path.Combine(base_path, ext + "\\self_id_prompt.wav");
                if (File.Exists(path))
                {
                    fname += path;
                }
                else
                {
                    fname += Utils.GetPrompt("EMPTY");
                }

            }
        }
        catch (Exception)
        {
            fname += Utils.GetPrompt("EMPTY");
        }

        Response.ContentType = "text/plain";
        byte[] data = Encoding.UTF8.GetBytes(fname);
        int length = data.Length;
        Response.AddHeader("Content-Length", length.ToString());
        Response.Expires = 0;
        Response.Cache.SetNoStore();
        Response.BufferOutput = true;
        Response.OutputStream.Write(data, 0, length);
//        Server.HtmlEncode(fname, Response.Output);
        Response.AddHeader("Connection", "close");
        Response.Flush();
    }

    private void GreetingMsgVM()
    {
        String fname = "x-path:";
        String ext = "";
        bool enabled = false;

        try
        {
            ext = Request.Params.Get("ext");
            DN dn = PhoneSystem.Root.GetDNByNumber(ext);
            if (dn != null && (dn is Extension))
            {
                Extension e = dn as Extension;
                enabled = e.VMEnabled;
                if (!enabled) fname += Utils.GetPrompt("BYE");
            }
        }
        catch (Exception e) { }

        if (enabled)
        try
        {
            string base_path = Utils.GetVoicemailPath();
            base_path = Path.GetFullPath(Path.Combine(base_path, @".\Data\"));
            
            if (ext == "")    fname += Utils.GetPrompt("RECYAMSG");
            else
            {
                String path = Path.Combine(base_path, ext + "\\savevmgreeting.wav");
                if (File.Exists(path))  fname += path;
                else                    fname += Utils.GetPrompt("RECYAMSG");
            }
        }
        catch (Exception)
        {
            fname += Utils.GetPrompt("RECYAMSG");
        }

        Response.ContentType = "text/plain";
        byte[] data = Encoding.UTF8.GetBytes(fname);
        int length = data.Length;
        Response.AddHeader("Content-Length", length.ToString());
        Response.Expires = 0;
        Response.Cache.SetNoStore();
        Response.BufferOutput = true;
        Response.OutputStream.Write(data, 0, length);
        //Server.HtmlEncode(fname, Response.Output);
        Response.AddHeader("Connection", "close");
        Response.Flush();
    }

    private void GetVMInfo(XmlNode form)
    {
        String ext = Request.Params["ext_num"];
        String act = Request.Params["action"];
        String fwdext = Request.Params["fwdext"];
        String xml_file = "";
        int listcount = 0;

        String file = "''", has_vm = "0", caller = "''", date = "''", time = "''", prmpt = "";
        FuncsNS.VMInfo vi = null;
        if (Session["VmailsList"] == null || Session["VM_pos"] == null)
        {
            file = "'" + Utils.GetPrompt("OPFAIL") + "'";
        }
        else
        {
            SortedList list = Session["VmailsList"] as SortedList;
            int pos = (int)Session["VM_pos"];

            vi = GetVMIAtPos(list, ref pos, act, ref prmpt, ref ext, ref fwdext);
            Session["VM_pos"] = pos;
            string vm_path = Utils.GetVoicemailPath() + @"\Extensions\";
            if (vi != null)
            {
                file = "'" + Path.Combine(vm_path + ext, vi.wav_file) + "'";
                has_vm = "1";
                caller = "'" + vi.caller + "'";
                date = "'" + vi.sCreated.Substring(0, 8) + "'";
                time = "'" + vi.sCreated.Substring(8, 4) + "'";
                xml_file = vi.xml_file;
            }
            else
            {
                file = "'" + Utils.GetPrompt(prmpt) + "'";
            }
            listcount = list.Count;
        }
        file = file.Replace("\\", "/");

        AddVar(form, "file", file);
        AddVar(form, "has_vm", has_vm);
        AddVar(form, "caller", caller);
        AddVar(form, "date", date);
        AddVar(form, "time", time);

        // add block and return values
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        XmlNode retN = form.OwnerDocument.CreateElement("return");
        form.AppendChild(blk);
        // no more messages to read
        //if (listcount < 1)
        //{
        //    AddAudio(blk, prmpt);
        //    AddAudio(blk, "YAHVNO");
        //    AddAudio(blk, "MSGS");
        //}

        Utils.AddAttr(retN, "namelist", "file has_vm caller date time");
        blk.AppendChild(retN);

        // now mark it as read
        if (xml_file.Length > 0)
        {
            vi.isRead = true;
            try
            {
                XmlDocument info = new XmlDocument();
                info.Load(xml_file);
                XmlNode vmsg_fl = info.SelectSingleNode("/VOICEMSG/FLAGS");
                if (vmsg_fl == null)
                {
                    vmsg_fl = info.CreateElement("FLAGS");
                    info.FirstChild.AppendChild(vmsg_fl);
                }
                int flg = int.Parse(vmsg_fl.InnerText);
                flg |= 1;
                vmsg_fl.InnerText = flg.ToString();

                XmlNode vmsg_hr = info.SelectSingleNode("/VOICEMSG/HEARD");
                if (vmsg_hr == null)
                {
                    vmsg_hr = info.CreateElement("HEARD");
                    info.DocumentElement.AppendChild(vmsg_hr);
                }
                vmsg_hr.InnerText =
                    String.Format(
                        "{0:yyyy}{0:MM}{0:dd}{0:HH}{0:mm}{0:ss}.{0:fff}",
                        DateTime.UtcNow);

                info.Save(xml_file);
            }
            catch (Exception)
            {
            }
        }
    }

    private FuncsNS.VMInfo GetVMIAtPos(SortedList list, ref int pos,
        string act, ref string prmpt, ref string ext, ref string fwdext)
    {
        FuncsNS.VMInfo vm = null;
        switch (act)
        {
            case "first":
                if (list.Count > 0)
                {
                    pos = 0;
                    vm = list.GetKey(0) as FuncsNS.VMInfo;
                }
                else
                    prmpt = "ENDOFMSGS";
                break;
            case "next":
                if (++pos < list.Count)
                {
                    vm = list.GetKey(pos) as FuncsNS.VMInfo;
                }
                else
                {
                    pos = list.Count - 1;
                    prmpt = "ENDOFMSGS";
                }
                break;
            case "prev":
                if (--pos >= 0)
                {
                    vm = list.GetKey(pos) as FuncsNS.VMInfo;
                }
                else
                {
                    pos = 0;
                    prmpt = "BEGOFMSGS";
                }
                break;
            case "rept":
                if (pos >= 0 && pos < list.Count)
                {
                    vm = list.GetKey(pos) as FuncsNS.VMInfo;
                }
                else
                    prmpt = "NOMSGSLCT";
                break;
            case "emptyprompt":
                    prmpt = "EMPTY";
                break;
            case "del":
                if (pos >= 0 && pos < list.Count)
                {
                    vm = list.GetKey(pos) as FuncsNS.VMInfo;
                    bool ret = DeleteVM(vm);
                    vm = null;
                    list.RemoveAt(pos);
                    prmpt = ret ? "MSGDLTD" : "NOMSGSTODEL";
                }
                else
                    prmpt = "NOMSGSLCT";
                break;
            case "fwd":
                if (pos >= 0 && pos < list.Count)
                {
                    DN dn = PhoneSystem.Root.GetDNByNumber(fwdext);
                    if (dn != null && (dn is Extension))
                    {
                        Extension e = dn as Extension;
                        if (!e.Enabled) prmpt = "EXTNOTENAB";
                        else if (!e.VMEnabled) prmpt = "VMLDSBL";
                        else
                        {
                            vm = list.GetKey(pos) as FuncsNS.VMInfo;
                            bool ret = ForwardVM(vm, ext, fwdext);
                            prmpt = ret ? "MSGFWDED" : "NOMSGSTOFWD";
                        }
                    }
                    else prmpt = "EXTNOTFOUND";
                    vm = null;
                }
                else
                    prmpt = "NOMSGSLCT";
                break;
        }
        return vm;
    }

    private void DeleteHeardVMs(XmlNode form)
    {
        XmlNode res = AddVar(form, "result", "0");
        XmlNode cnt = AddVar(form, "count", "0");

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        if (Session["VmailsList"] == null || Session["VM_pos"] == null)
            goto Exit;

        int count = 0;
        bool error = false;

        SortedList list = Session["VmailsList"] as SortedList;
        for (int i = list.Count - 1; i >= 0; i--)
        {
            FuncsNS.VMInfo vi = list.GetKey(i) as FuncsNS.VMInfo;
            if (vi.isRead)
            {
                if (DeleteVM(vi))
                    count++;
                else
                    error = true;
                list.RemoveAt(i);
            }
        }
        int pos = (int)Session["VM_pos"];
        if (pos >= list.Count)
            pos = list.Count > 0 ? list.Count - 1 : 0;
        Session["VM_pos"] = pos;

        cnt.Attributes["expr"].Value = count.ToString();
        res.Attributes["expr"].Value = error ? "2" : "1";

    Exit:
        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
        Utils.AddAttr(retN, "namelist", "result count");
    }

    private bool DeleteVM(FuncsNS.VMInfo vi)
    {
        String path = Path.GetDirectoryName(vi.xml_file);
        try
        {
            File.Delete(vi.xml_file);
            File.Delete(Path.Combine(path, vi.wav_file));
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }
    private bool ForwardVM(FuncsNS.VMInfo vi, String curext, String fwdext)
    {
        String path = Path.GetDirectoryName(vi.xml_file);
        try
        {
            // copy wav file
            String cxml = vi.xml_file;
            cxml = cxml.Replace("\\"+curext+"\\", "\\"+fwdext+"\\");
            Utils.makeFolder(Path.GetDirectoryName(cxml));
            //File.Copy( vi.xml_file, cxml);

            String cwav = Path.Combine(path, vi.wav_file);
            cwav = cwav.Replace("\\" + curext + "\\", "\\" + fwdext + "\\");
            File.Copy(Path.Combine(path, vi.wav_file), cwav);

            // reset 'heard' flag and add fwd info
            XmlDocument inf = new XmlDocument();
            inf.Load(vi.xml_file);
            // reset all flags, (heard, removed, notifSent and ..
            inf.SelectSingleNode("/VOICEMSG/FLAGS").InnerText = "0";
            // create fwding info
            XmlNode fwdnode = inf.CreateElement("FORWARDED");
            ((XmlElement)fwdnode).SetAttribute("BY", curext);
            ((XmlElement)fwdnode).SetAttribute("TO", fwdext);
            ((XmlElement)fwdnode).SetAttribute("AT", String.Format("{0:yyyyMMddHHmmss.ff}", DateTime.Now));

            // include any previous fwd info

            XmlNode forwarded_xmln = inf.SelectSingleNode("/VOICEMSG/FORWARDED");
            XmlNode vmnode = inf.SelectSingleNode("/VOICEMSG");
            if (forwarded_xmln != null)
            {
                vmnode.RemoveChild(forwarded_xmln);
                fwdnode.AppendChild(inf.ImportNode(forwarded_xmln, true));
            }

            inf.SelectSingleNode("/VOICEMSG").AppendChild(fwdnode);
            
            //inf.Save(cxml) ;
            System.IO.StreamWriter outstrm = new StreamWriter(cxml, false);
            inf.Save(outstrm);
            outstrm.Close();
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }
    private bool SavePin(XmlNode form)
    {
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);
        try
        {
            Extension extDn = PhoneSystem.Root.GetDNByNumber(Request.Params["ext"]) as Extension;
            String Pin = Request.Params["pn"];
            if (Pin[Pin.Length - 1] == '#')
                Pin = Pin.Substring(0, Pin.Length - 1);
            extDn.VMPIN = Pin;
            extDn.Save();
            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=NEW_PIN_CODE");
            aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=SAVED");
        }
        catch (Exception e)
        {
            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=OPFAIL");
        }

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);

        return true;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        String func = Request.Params.Get("func");
        if (func == null)
            func = "unknown";

        XmlDocument doc = new XmlDocument();
        XmlNode vxml = doc.CreateElement("vxml");
        doc.AppendChild(vxml);
        Utils.AddAttr(vxml, "version", "2.0");
        Utils.AddAttr(vxml, "application", "root.vxml");

        XmlNode form = doc.CreateElement("form");
        vxml.AppendChild(form);
        Utils.AddAttr(form, "id", func);

        switch (func)
        {
            case "GetParam":
                {
					String param = Request.Params.Get("pname");

					if (!String.IsNullOrEmpty(param)&&param=="VMDIALOUTENABLED")
						GetParam(form);
				}
                break;
            case "CheckExt":
                CheckExt(form);
                break;
            case "CheckPin":
                CheckPin(form);
                break;
            case "enter_vm":
                EnterVM(form);
                break;
            case "play_vm":
                PlayVM();
                return;
            case "vm_greeting_msg":
                GreetingMsgVM();
                return;
            case "self_id_msg":
                SelfIdMsgVM();
                return;
            case "vm_info":
                GetVMInfo(form);
                break;
            case "say_pn":
                SayPhoneNumber(form);
                break;
            case "say_dt":
                SayDateTime(form);
                break;
            case "say_num":
                SayNumber(form);
                break;
            case "say_response":
                SayResponse(form);
                break;
            case "del_heard":
                DeleteHeardVMs(form);
                break;
            case "save_pin":
                SavePin(form);
                break;
            case "say_pin":
                SayPinNumber(form);
                break;
            case "restore_grt":
                RestoreGreetings(form);
                break;
            case "remove_selfid":
                RemoveSelfId(form);
                break;
            case "make_call":
                MakeCall(form);
                break;
            case "DeleteVMFile":
                DeleteVMFile(form);
                break;
            case "CurrentProfile":
                SetExtensionCurrentProfile(form);
                break;
            case "directtransfer":
                TransferToNumber(form);
                break;
                
        }

        Response.BufferOutput = true;
        Response.Expires = 0;
        Response.Cache.SetNoStore();

        doc.Save(Response.OutputStream);
        Response.AddHeader("Connection", "close");
        Response.Flush();

        doc.Save(Console.Out);
    }

    private void RemoveSelfId(XmlNode form)
    {
        String ext;
        try
        {
            ext = Request.Params.Get("ext");
            string base_path = Utils.GetVoicemailPath();
            base_path = Path.GetFullPath(Path.Combine(base_path, @".\Data\"));
            String path = Path.Combine(base_path, ext + "\\self_id_prompt.wav");
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            XmlNode blk = form.OwnerDocument.CreateElement("block");
            form.AppendChild(blk);

            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=MSGDLTD");
            XmlNode retN = form.OwnerDocument.CreateElement("return");
            blk.AppendChild(retN);
        }
        catch (Exception)
        {

        }
    }

    private void RestoreGreetings(XmlNode form)
    {
        String ext;
        try
        {
            ext = Request.Params.Get("ext");
            string base_path = Utils.GetVoicemailPath();
            base_path = Path.GetFullPath(Path.Combine(base_path, @".\Data\"));
            String path = Path.Combine(base_path, ext + "\\savevmgreeting.wav");
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            XmlNode blk = form.OwnerDocument.CreateElement("block");
            form.AppendChild(blk);

            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=STDMSGRST");
            XmlNode retN = form.OwnerDocument.CreateElement("return");
            blk.AppendChild(retN);
        }
        catch (Exception)
        {

        }
    }

    private void SayNumber(XmlNode form)
    {
        String num_str = Request.Params["num"];
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        int num = num_str == null ? 0 : int.Parse(num_str);
        SayNumber(blk, num, false);

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
    }

    private void SayResponse(XmlNode form)
    {
        String response_str = Request.Params["resp"];

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        //String paramname = "IVRORDER_" + response_str; //QUEINTRO
        //TCX.Configuration.Parameter prm = PhoneSystem.Root.GetParameterByName(paramname);
        //string[] items = prm.Value.Split('+');
        try
        {
            if (ResponseOrder.Inst.mResponses.Count < 1)
            {
                PromptSet ps = Utils.GetPromptSet();
                String path = Path.Combine(Utils.AppDataPath, @"Data\Ivr\Prompts");
                String setpath = path;
                setpath = Path.Combine(Path.Combine(setpath, @"Sets"), ps.Folder);
                setpath = Path.Combine(setpath, "ResponseOrdering.xml");

                if (!ResponseOrder.Inst.Load(setpath))
                {
                    // try default path if set doesnt contain ordering
                    ResponseOrder.Inst.Load(Path.Combine(path, "ResponseOrdering.xml")); 
                }
            }

            string orderstr = ResponseOrder.Inst.GetOrder(response_str.ToUpper());
            string[] items = orderstr.Split('+');
            foreach (string itm in items)
            {

                if (itm.ToUpper() == "$NUM$")
                {
                    String num_str = Request.Params["num"];
                    int num = num_str == null ? 0 : int.Parse(num_str);
                    SayNumber(blk, num, false);
                }
                else if (itm.ToUpper() == "$PIN$")
                {
                    SayPinNumber(form, blk);
                }
                else if (itm.ToUpper() == "$DATE$")
                {
                    SayDate(form, blk);
                }
                else if (itm.ToUpper() == "$TIME$")
                {
                    SayTime(form, blk);
                }
                else if (itm.ToUpper() == "$PHONENUM$")
                {
                    SayPhoneNumber(form, blk);
                }
                else if (itm.ToUpper() == "$MESSAGES$")
                {
                    String nums = Request.Params["messages"];
                    int nummsgs = 0;
                    try { nummsgs = int.Parse(nums); }
                    catch { }
                    if (nummsgs == 1) AddAudio(blk, "MSG");
                    else AddAudio(blk, "MSGS");
                }
                else if (itm.ToUpper() == "$PROMPT$")
                {
                    String prm = Request.Params["prompt"];
                    AddAudio(blk, prm);
                }
                else if (itm.ToUpper() == "$VMAILAUDIO$")
                {
                    String prm = Request.Params["vmaudio"];
                    AddAudioFile(blk, Path.Combine(Utils.GetVoicemailPath(), prm));
                }
                else
                    AddAudio(blk, itm);
            }
        }
        catch { }

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
    }

    private DateTime StringToDateTime(String date, String time)
    {
        DateTime dt = DateTime.UtcNow;
        int yr = dt.Year;
        int mn = dt.Month;
        int day = dt.Day;
        int hr = dt.Hour;
        int min = dt.Minute;

        if (date != null && date.Length == 8)
        {
            try
            {
                DateTime da = new DateTime(
                    int.Parse(date.Substring(0, 4)),
                    int.Parse(date.Substring(4, 2)),
                    int.Parse(date.Substring(6, 2)));
                yr = da.Year;
                mn = da.Month;
                day = da.Day;
            }
            catch (Exception)
            { }
        }

        if (time != null && time.Length == 4)
        {
            hr = int.Parse(time.Substring(0, 2));
            min = int.Parse(time.Substring(2, 2));
            if (hr < 0 || hr > 23)
                hr = dt.Hour;
            if (min < 0 || min > 59)
                min = dt.Minute;
        }

        DateTime d = new DateTime(yr, mn, day, hr, min, 0, DateTimeKind.Utc);
        d = d.ToLocalTime();
        return d;
    }
    // used by SayResponse()
    private void SayDate(XmlNode form, XmlNode blk)
    {
        String date = Request.Params["date"];
        String time = Request.Params["time"];

        DateTime d = StringToDateTime(date, time);
        PromptSet ps = Utils.GetPromptSet();

        SayDate(blk, d, ps.UseAlternateNumberPronunciation);
    }

    // used by SayResponse()
    private void SayTime(XmlNode form, XmlNode blk)
    {
        String date = Request.Params["date"];
        String time = Request.Params["time"];
        String tfmt = Request.Params["tfmt"];
        bool b24h = true;
        if (tfmt != null)
        { b24h = tfmt == "2" ? true : false; }

        DateTime d = StringToDateTime(date, time);
        PromptSet ps = Utils.GetPromptSet();

        SayTime(blk, d, ps.UseAlternateNumberPronunciation, b24h);
    }

    private void SayDateTime(XmlNode form)
    {
        String date = Request.Params["date"];
        String time = Request.Params["time"];
        String tfmt = Request.Params["tfmt"];
        bool b24h = true;
        if (tfmt != null)
        { b24h = tfmt == "2" ? true : false; }

        PromptSet ps = Utils.GetPromptSet();

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        DateTime d = StringToDateTime(date, time);

        SayDate(blk, d, ps.UseAlternateNumberPronunciation);
        AddAudio(blk, "EMPTY");
        SayTime(blk, d, ps.UseAlternateNumberPronunciation, b24h);

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
    }

    private void SayDate(XmlNode blk, DateTime d, bool germ)
    {
        DateTime now = DateTime.Now;
        TimeSpan s = now.Date.Subtract(d.Date);
        if (s.Days > 1)
        {
            // say day/month
            if (!germ)
            {
                SayMonth(blk, d.Month);
                SayOrdinal(blk, d.Day, germ);
            }
            else
            {
                SayOrdinal(blk, d.Day, germ);
                SayMonth(blk, d.Month);
            }

            if (d.Year != now.Year)
            { // say year
                if (!germ)
                {
                    int centure = d.Year / 100;
                    if (centure % 10 != 0)
                    {//19:99
                        SayNumber(blk, centure, germ);
                        if (d.Year % 100 < 10)
                        {
                            AddAudio(blk, "TM_OU");
                            SayNumber(blk, d.Year % 10, germ);
                        }
                        else
                            SayNumber(blk, d.Year % 100, germ);
                    }
                    else
                    {
                        SayNumber(blk, d.Year, germ);
                    }
                }
                else
                {
                    SayNumber(blk, d.Year, germ);
                }
            }
        }
        else if (s.Days == 1)
        {
            AddAudio(blk, "YESTERDAY");
        }
        else
        {
            AddAudio(blk, "TODAY");
        }
    }

    private void SayTime(XmlNode blk, DateTime d, bool germ, bool b24h)
    {
        if (!germ)
        {
            if (b24h)
            {
                if (d.Hour > 0)
                {
                    SayNumber(blk, d.Hour, germ);
                }
                if (d.Minute > 0)
                {
                    if (d.Minute < 10 && d.Hour > 0)
                        AddAudio(blk, "TM_OU");
                    SayNumber(blk, d.Minute, germ);
                    if (d.Hour == 0)
                    {
                        AddAudio(blk, d.Minute == 1 ? "TM_MINUTE" : "TM_MINUTES");
                        AddAudio(blk, "TM_PAST");
                    }
                }
                else if (d.Hour > 0)
                    AddAudio(blk, "TM_OCLOCK");

                if (d.Hour == 0)
                    AddAudio(blk, "TM_MIDNIGHT");
            }
            else
            {//12h
                bool bAM = d.Hour < 12;
                int hr = d.Hour;
                if (hr == 0)
                    hr = 12;
                else if (hr > 12)
                    hr -= 12;
                SayNumber(blk, hr, germ);
                if (d.Minute > 0)
                {
                    if (d.Minute < 10)
                        AddAudio(blk, "TM_OU");
                    SayNumber(blk, d.Minute, germ);
                }
                AddAudio(blk, bAM ? "TM_AM" : "TM_PM");
            }
        }
        else
        {
            if (b24h)
            {
                SayNumber(blk, d.Hour, germ);
                AddAudio(blk, "TM_OCLOCK");
                if (d.Minute > 0)
                    SayNumber(blk, d.Minute, germ);
            }
            else
            {
                string ampm = d.Hour < 12 ? "TM_AM" : "TM_PM";
                int hr = d.Hour;
                if (hr == 0)
                    hr = 12;
                else if (hr > 12)
                    hr -= 12;

                SayNumber(blk, hr, germ);
                AddAudio(blk, "TM_OCLOCK");
                if (d.Minute > 0)
                    SayNumber(blk, d.Minute, germ);
                AddAudio(blk, ampm);
            }
        }
    }

    private void SayNumber(XmlNode blk, int num, bool germ)
    {
        if (num == 0)
        {
            AddAudio(blk, "NUM_0");
            return;
        }
        bool and = false;
        if (num >= 1000000)
        {
            SayNumber(blk, (num / 1000000) % 1000, germ);
            AddAudio(blk, "NUM_MLN");
            num %= 1000000;
            and = true;
        }

        if (num >= 1000)
        {
            SayNumber(blk, (num / 1000) % 1000, germ);
            AddAudio(blk, "NUM_THSND");
            num %= 1000;
            and = true;
        }

        if (num >= 100)
        {
            AddAudio(blk, "NUM_" + num / 100);
            AddAudio(blk, "NUM_HNDR");
            num %= 100;
        }

        if (and && num > 0)
        {
            AddAudio(blk, "NUM_AND");
        }

        // now, num < 1000
        if (num > 0 && num < 20)
        {
            AddAudio(blk, "NUM_" + num);
        }
        else if (num >= 20)
        {
            int ones = num % 10;
            int tens = num - ones;
            if (!germ)
            {
                if (ones > 0)
                {
                    AddAudio(blk, "NUM_" + tens + "+");
                    AddAudio(blk, "NUM_" + ones);
                }
                else
                {
                    AddAudio(blk, "NUM_" + tens);
                }
            }
            else
            {
                if (ones > 0)
                {
                    AddAudio(blk, "NUM_" + ones);
                    AddAudio(blk, "NUM_AND");
                }
                AddAudio(blk, "NUM_" + tens);
            }
        }
    }

    private void SayOrdinal(XmlNode blk, int num, bool germ)
    {
        if (num == 0)
        {
            AddAudio(blk, "NUM_0");
            return;
        }
        bool and = false;
        if (num >= 1000000)
        {
            SayNumber(blk, (num / 1000000) % 1000, germ);
            AddAudio(blk, "NUM_MLN");
            num %= 1000000;
            and = true;
        }

        if (num >= 1000)
        {
            SayNumber(blk, (num / 1000) % 1000, germ);
            AddAudio(blk, "NUM_THSND");
            num %= 1000;
            and = true;
        }

        if (num >= 100)
        {
            AddAudio(blk, "NUM_" + num / 100);
            AddAudio(blk, "NUM_HNDR");
            num %= 100;
        }

        if (and && num > 0)
        {
            AddAudio(blk, "NUM_AND");
        }

        // now, num < 1000
        if (num > 0 && num < 20)
        {
            AddAudio(blk, "NUM_" + num + "_O");
        }
        else if (num >= 20)
        {
            int ones = num % 10;
            int tens = num - ones;
            if (!germ)
            {
                if (ones > 0)
                {
                    AddAudio(blk, "NUM_" + tens + "+");
                    AddAudio(blk, "NUM_" + ones + "_O");
                }
                else
                {
                    if (tens <= 30)
                        AddAudio(blk, "NUM_" + tens + "_O");
                    else
                        AddAudio(blk, "NUM_" + tens); //40,50,.. do not have ordinal prompts?!
                }
            }
            else
            {
                if (ones > 0)
                {
                    AddAudio(blk, "NUM_" + ones);
                    AddAudio(blk, "NUM_AND");
                }
                if (tens <= 30)
                    AddAudio(blk, "NUM_" + tens + "_O");
                else
                    AddAudio(blk, "NUM_" + tens);
            }
        }
    }

    private void SayMonth(XmlNode blk, int month)
    {
        String mon_str = String.Format("TM_MON_{0:00}", month);
        AddAudio(blk, mon_str);
    }

    private void SayPhoneNumber(XmlNode form)
    {
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        SayPhoneNumber(form, blk);

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
    }
    private void SayPhoneNumber(XmlNode form, XmlNode blk)
    {
        String pn = Request.Params["pn"];
        pn = pn.Trim();

        if (pn == null)
        {
            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=INV_NUM");
            return;
        }

        String digit = "";
        foreach (char c in pn)
        {
            if (c >= '0' && c <= '9')
            {
                digit = "NUM_" + c;
            }
            else if (c == '*')
            {
                digit = "NUM_STAR";
            }
            else if ( (c == '#') || (c == '+') )
            {
                digit = "NUM_AND";  //TODO: add NUM_POUND prompt
            }
            else
                break;

            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", Utils.GetPrompt(digit));
        }

    }

    // used from SayResponse
    private void SayPinNumber(XmlNode form, XmlNode blk)
    {
        String pn = Request.Params["pin"];

        if (pn == null)
        {
            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", "GetPrompt.aspx?id=INV_NUM");
            return;
        }

        String digit = "";
        foreach (char c in pn)
        {
            if (c >= '0' && c <= '9')
            {
                digit = "NUM_" + c;
            }
            else
                break;

            XmlNode aud = form.OwnerDocument.CreateElement("audio");
            blk.AppendChild(aud);
            Utils.AddAttr(aud, "src", Utils.GetPrompt(digit));
        }
    }

    private void SayPinNumber(XmlNode form)
    {
        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);

        SayPinNumber(form, blk);

        XmlNode retN = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(retN);
    }


    private void DeleteVMFile(XmlNode form)
    {
        String filepath = Request.Params["vmfile"];

        XmlNode log = form.OwnerDocument.CreateElement("log");
        form.AppendChild(log);

        try
        {
            filepath = Path.Combine( Utils.GetVoicemailPath(), filepath);
            File.Delete(filepath);
        }
        catch (Exception excp)
        {
            log.InnerText = excp.ToString();
            return;
        }
        log.InnerText = "Successful deleted " + filepath;
    }

    private void SetExtensionCurrentProfile(XmlNode form)
    {
        String sDN = Request.Params["from"];
        String profname = Request.Params["profilename"];
        XmlNode log = form.OwnerDocument.CreateElement("log");
        form.AppendChild(log);
        bool success = false;

        Extension ext;
        try
        {
            profname = profname.Replace('_', ' ');
            PhoneSystem ps = PhoneSystem.Root;
            DN dn = ps.GetDNByNumber(sDN);
            if (dn == null || !(dn is Extension)) return;
            ext = (Extension)dn;

            // set current profile
            foreach (FwdProfile fp in ext.FwdProfiles)
            {
                if (fp.Name == profname)
                {
                    ext.CurrentProfile = fp;
                    ext.Save();
                    success = true;
                    break;
                }
            }
        }
        catch (Exception excp)
        {
            log.InnerText = excp.ToString();
        }

        XmlNode blk = form.OwnerDocument.CreateElement("block");
        form.AppendChild(blk);
        if (success)
        {
            log.InnerText = "profile status for " + sDN + " is set to " + profname;
            if (profname == "Available")        AddAudio(blk, "ST_AVAILABLE_SET");
            else if (profname == "Away")        AddAudio(blk, "ST_AWAY_SET");
            else if (profname == "Out of office") AddAudio(blk, "ST_OUTOFFICE_SET");
            else if (profname == "Custom 1")    AddAudio(blk, "ST_CUSTOM1_SET");
            else if (profname == "Custom 2")    AddAudio(blk, "ST_CUSTOM2_SET");
        }
        else
            AddAudio(blk, "OPFAIL");

        XmlNode ret = form.OwnerDocument.CreateElement("return");
        blk.AppendChild(ret);
    }

    private void MakeCall(XmlNode form)
    {
        String from = Request.Params["from"];
        String to = Request.Params["to"];
        String pin = Request.Params["pin"];

        XmlNode log = form.OwnerDocument.CreateElement("log");
        form.AppendChild(log);

        try
        {
            DN dn = PhoneSystem.Root.GetDNByNumber(from);
            if (dn == null || !(dn is Extension))
            {
                log.InnerText = "Originating number is not an extension!";
                return;
            }
            if (pin == null || (dn as Extension).VMPIN != pin)
            {
                log.InnerText = "Authorization failed! PIN doesn't match";
                return;
            }

            PBXConnection api = new PBXConnection();
            api.MakeCall(from, to);
        }
        catch (Exception excp)
        {
            log.InnerText = excp.ToString();
            return;
        }
        log.InnerText = "Successful make call from " + from + " to " + to;
    }

}
