// ----------------------------------------------------------------------------
// Copyright 2007-2017, GeoTelematic Solutions, Inc.
// All rights reserved
// ----------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ----------------------------------------------------------------------------
// Change History:
//  2009/07/01  Martin D. Flynn
//     -Initial release
// ----------------------------------------------------------------------------
package org.opengts.db;

import java.lang.*;
import java.util.*;
import java.io.*;

import org.opengts.util.*;
import org.opengts.dbtools.*;
import org.opengts.db.tables.*;

public class ReportURL
{

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

    public  static final String RPTARG_ACCOUNT          = "account";    // Constants.PARM_ACCOUNT
    public  static final String RPTARG_USER             = "user";       // Constants.PARM_USER
    public  static final String RPTARG_ENCPASS          = "encpass";    // Constants.PARM_ENCPASS

    public  static final String RPTARG_DEVICE           = "device";     // Constants.PARM_DEVICE
    public  static final String RPTARG_GROUP            = "group";      // Constants.PARM_GROUP
    
    public  static final String RPTARG_DATE_FR[]        = new String[] { "date_fr"      , "fr"    };    // Calendar.PARM_RANGE_FR
    public  static final String RPTARG_DATE_TO[]        = new String[] { "date_to"      , "to"    };    // Calendar.PARM_RANGE_TO
    public  static final String RPTARG_DATE_TZ[]        = new String[] { "date_tz"      , "tz"    };    // Calendar.PARM_TIMEZONE
    
    public  static final String RPTARG_REPORT[]         = new String[] { "r_report"     , "rpt"   };
    public  static final String RPTARG_LIMIT[]          = new String[] { "r_limit"      , "lim"   };
    public  static final String RPTARG_LIMIT_TYPE[]     = new String[] { "r_limType"    , "ltp"   };
    
    public  static final String RPTARG_FORMAT[]         = new String[] { "r_format"     , "fmt"   };
    public  static final String RPTARG_EMAIL[]          = new String[] { "r_emailAddr"  , "email" };

    public  static final String URLARG_RTP              = "rtp_";

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

    // -- formats
    public static final String FORMAT_HTML              = "html";
    public static final String FORMAT_XML               = "xml";
    public static final String FORMAT_CSV               = "csv";
    public static final String FORMAT_PDF               = "pdf";
    public static final String FORMAT_XLS               = "xls";
    public static final String FORMAT_XLSX              = "xlsx";
    public static final String FORMAT_TXT               = "txt";
    public static final String FORMAT_SOAPXML           = "soapxml";
    public static final String FORMAT_EHTML             = "ehtml";      // embedded HTML (no external links)
    public static final String FORMAT_CUSTOM            = "custom";
    public static final String FORMAT_SCHEDULE          = "sched";
    public static final String FORMAT_URL               = "url";
    public static final String FORMAT_EMAIL             = "email";
    public static final String FORMAT_CALLBACK          = "callback";

    // -- ACLs
    public static final String _ACL_FORMAT_NON_HTML     = "nonHtml";  // non-html formats
    public static final String _ACL_FORMAT_HTML         = "html";     // always enabled
    public static final String _ACL_FORMAT_EMAIL        = "email";    // ReportURL.FORMAT_EHTML
    public static final String _ACL_FORMAT_CSV          = "csv";      // ReportURL.FORMAT_CSV
    public static final String _ACL_FORMAT_XML          = "xml";      // ReportURL.FORMAT_XML
    public static final String _ACL_FORMAT_XLS          = "xls";      // ReportURL.FORMAT_XLS
    public static final String _ACL_FORMAT_SCHEDULE     = "sched";    // ReportURL.FORMAT_SCHEDULE (not yet supported)
    public static final String _ACL_FORMAT_PDF          = "pdf";      // ReportURL.FORMAT_PDF (not yet supported)
    public static final String _ACL_FORMAT_CUSTOM       = "custom";   // ReportURL.FORMAT_CUSTOM (not yet supported)
    public static final String _ACL_LIST[]              = new String[] { 
      //_ACL_FORMAT_HTML,       <-- always enabled
        _ACL_FORMAT_EMAIL,
        _ACL_FORMAT_CSV,
        _ACL_FORMAT_XML,
        _ACL_FORMAT_XLS,
      //_ACL_FORMAT_SCHEDULE,   <-- not yet supported
      //_ACL_FORMAT_PDF,        <-- not yet supported
      //_ACL_FORMAT_CUSTOM,     <-- not yet supported
    };

    public enum Format implements EnumTools.IntValue, EnumTools.StringValue {
        HTML     (  0, ReportURL.FORMAT_HTML    , null                ), // MIME: "text/html" (default)
        XML      (  1, ReportURL.FORMAT_XML     , _ACL_FORMAT_XML     ), // MIME: "text/xml"
        CSV      (  2, ReportURL.FORMAT_CSV     , _ACL_FORMAT_CSV     ), // MIME: "text/csv"
        XLS      (  3, ReportURL.FORMAT_XLS     , _ACL_FORMAT_XLS     ), // MIME: "application/vnd.ms-excel"
        XLSX     (  4, ReportURL.FORMAT_XLSX    , _ACL_FORMAT_XLS     ), // MIME: "application/vnd.ms-excel"
        TXT      (  5, ReportURL.FORMAT_TXT     , _ACL_FORMAT_CSV     ), // MIME: "text/plain" (csv format)
        SOAP     (  6, ReportURL.FORMAT_SOAPXML , _ACL_FORMAT_XML     ), // 
        EHTML    (  7, ReportURL.FORMAT_EHTML   , _ACL_FORMAT_EMAIL   ), // 
        CUSTOM   (  8, ReportURL.FORMAT_CUSTOM  , _ACL_FORMAT_CUSTOM  ), // 
        SCHEDULE (  9, ReportURL.FORMAT_SCHEDULE, _ACL_FORMAT_SCHEDULE), // 
        URL      ( 10, ReportURL.FORMAT_URL     , null                ), // 
        EMAIL    ( 11, ReportURL.FORMAT_EMAIL   , _ACL_FORMAT_EMAIL   ), // 
        CALLBACK ( 12, ReportURL.FORMAT_CALLBACK, null                ), // 
        PDF      ( 13, ReportURL.FORMAT_PDF     , _ACL_FORMAT_PDF     ); // 
        // ---
        private int      vv = 0;
        private String   ff = null;
        private String   aa = null;
        Format(int v, String f, String a) { vv = v; ff = f; aa = a; }
        public int     getIntValue()      { return vv; }
        public String  getACL()           { return aa; }
        public String  getFormat()        { return ff; }
        public String  getStringValue()   { return this.getFormat(); }
        public String  toString()         { return this.getFormat(); }
    }

    /**
    *** Gets the Format enum value for the specified name
    *** @param name The name of the Format
    *** @return The Format, or null if the name is invalid
    **/
    public static Format getFormat(String name)
    {
        return EnumTools.getValueOf(Format.class, name, (Format)null);
    }

    /**
    *** Gets the ACL name for the specified format
    **/
    public static String getFormatACL(String name)
    {
        Format fmt = ReportURL.getFormat(name);
        return (fmt != null)? fmt.getACL() : null;
    }

    /**
    *** Return true if the specified user has read-access to the specified report format
    **/
    public static boolean hasFormatReadAccess(User user, BasicPrivateLabel privLabel, String aclName, String fmtName)
    {

        /* pre-checks */
        if (privLabel == null) {
            // -- BasicPrivateLabel is required
            return false;
        } else
        if (StringTools.isBlank(aclName)) {
            // -- no ACL name to check
            return false;
        }

        /* get format */
        Format fmt = ReportURL.getFormat(fmtName);
        if (fmt == null) {
            // -- invalid format
            return false;
        }

        /* is HTML? */
        if (fmt.equals(ReportURL.Format.HTML)) {
            // -- always has access to HTML format
            return true;
        }

        /* non-html formats allowed? */
        if (!privLabel.hasReadAccess(user, AclEntry.CreateAclName(aclName,_ACL_FORMAT_NON_HTML))) {
            // -- non-html format not allowed
            return false;
        }

        /* check ACL */
        String subACL = fmt.getACL();
        if (StringTools.isBlank(subACL)) {
            // -- no sub ACL, assume true
            return true;
        }

        /* hasReadAccess? */
        return privLabel.hasReadAccess(user, AclEntry.CreateAclName(aclName,subACL));

    }

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

    public  static final String PARM_PAGE           = "page";        // org.opengts.war.tools.CommonServlet.PARM_PAGE;

    public  static final String PAGE_REPORT_SHOW    = "report.show"; // org.opengts.war.track.Constants.PAGE_REPORT_SHOW;

    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------
    
    /**
    *** Return a URL for the EventDetail report, for the specified Device
    **/
    public static URIArg createEventDetailReportURL(
        String userID, boolean inclPass,
        Device dev, long timestamp,
        String baseURL)
    {

        /* no Device? */
        if (dev == null) { 
            return null; 
        }
        String deviceID  = dev.getDeviceID();
        String groupID   = null;

        /* get Account */
        String  accountID = dev.getAccountID();
        Account account   = dev.getAccount();
        TimeZone acctTZ   = account.getTimeZone(null/*default*/);

        /* get User */
        User user = null;
        try {
            if (StringTools.isBlank(userID)) {
                String uid = account.getDefaultUser();
                if (!StringTools.isBlank(uid)) {
                    userID = uid;
                } else {
                    userID = User.getAdminUserID();
                }
            }
            user = User.getUser(account, userID); // may return null
        } catch (DBException dbe) {
            Print.logException("Reading User: " + accountID + "/" + userID, dbe);
            user = null;
        }
        TimeZone TZ = (user != null)? user.getTimeZone(acctTZ) : acctTZ;

        /* get password */
        String encPass = null;
        if (inclPass) {
            if (!StringTools.isBlank(userID) && (user != null)) {
                encPass = user.getDecodedPassword(null/*BasicPrivateLabel*/);
            } else {
                encPass = account.getDecodedPassword(null/*BasicPrivateLabel*/);
            }
        }

        /* Event dates */
        String date_tz = ""; // (user != null)? user.getTimeZone() : account.getTimeZone(); // String name
        String date_fr = null;
        String date_to = null;
        if (timestamp > 0L) {
            date_fr = String.valueOf(timestamp - 2L);
            date_to = String.valueOf(timestamp + 2L);
        } else {
            date_fr = "last";
            date_to = "from";
        }

        /* report */
        String r_report  = ""; // default ("EventDetail")
        String r_limit   = ""; // default (1000)
        String r_limType = ""; // default ("last")
        String r_format  = ""; // default ("http")

        /* create URL */
        boolean rtpEncode = true;
        return  ReportURL.createReportURL(
            baseURL, rtpEncode,
            accountID, userID, encPass,
            deviceID, groupID,
            date_fr, date_to, date_tz,
            r_report,
            r_limit, r_limType,
            r_format);

    }

    // ------------------------------------------------------------------------
    // account=<account> user=<user>
    // r_report=<report> 
    // device=<device> | group=<group>
    // date_fr=<ts> date_to=<ts> date_tz=<tz>
    // r_limit=<limit> r_limType=last|first
    // format=html|csv|xml

    public static URIArg createReportURL(URIArg rptURL, boolean rtpEncode)
    {
        if (rptURL == null) { return null; }
        String  baseURL   = rptURL.getURI();
        RTProperties rtp  = rptURL.getArgProperties();
        // authorization
        String  accountID = rtp.getString(RPTARG_ACCOUNT     , "");
        String  userID    = rtp.getString(RPTARG_USER        , "");
        String  encPass   = rtp.getString(RPTARG_ENCPASS     , "");
        // device/group report
        String  deviceID  = rtp.getString(RPTARG_DEVICE      , "");
        String  groupID   = rtp.getString(RPTARG_GROUP       , "");
        // date range
        String  date_fr   = rtp.getString(RPTARG_DATE_FR     , "");
        String  date_to   = rtp.getString(RPTARG_DATE_TO     , "");
        String  date_tz   = rtp.getString(RPTARG_DATE_TZ     , "");
        // report attributes
        String  r_report  = rtp.getString(RPTARG_REPORT      , "");
        String  r_limit   = rtp.getString(RPTARG_LIMIT       , "");
        String  r_limType = rtp.getString(RPTARG_LIMIT_TYPE  , "");
        String  r_format  = rtp.getString(RPTARG_FORMAT      , "");
        // create report url
        return  ReportURL.createReportURL(
            baseURL, rtpEncode,
            accountID, userID, encPass,
            deviceID, groupID,
            date_fr, date_to, date_tz,
            r_report,
            r_limit, r_limType,
            r_format);
    }

    public static URIArg createReportURL(
        String baseURL, boolean rtpEncode,
        String accountID, String userID, String encPass,
        String deviceID, String groupID,
        String date_fr, String date_to, String date_tz,
        String r_report,
        // remaining args are optional
        String r_limit, String r_limType,
        String r_format)
    {

        /* URL */
        URIArg url = new URIArg(baseURL);
        if (!StringTools.isBlank(accountID)) {
            url.addArg(RPTARG_ACCOUNT, accountID);
        }
        if (!StringTools.isBlank(userID)) {
            url.addArg(RPTARG_USER, userID);
        }
        if (!StringTools.isBlank(encPass) && !encPass.equals(Account.BLANK_PASSWORD)) {
            url.addArg(RPTARG_ENCPASS, encPass);
        }

        /* create RTP */
        RTProperties rtp = new RTProperties();
        rtp.setString(PARM_PAGE, PAGE_REPORT_SHOW);
        // device=
        if (!StringTools.isBlank(deviceID)) {
            rtp.removeProperties(RPTARG_DEVICE);
            rtp.setString(RPTARG_DEVICE, deviceID);
        }
        // group=
        if (!StringTools.isBlank(groupID)) {
            rtp.removeProperties(RPTARG_GROUP);
            rtp.setString(RPTARG_GROUP, groupID);
        }
        // date_fr=
        if (!StringTools.isBlank(date_fr)) {
            rtp.removeProperties(RPTARG_DATE_FR);
            rtp.setString(RPTARG_DATE_FR[1], date_fr);
        }
        // date_to=
        if (!StringTools.isBlank(date_to)) {
            rtp.removeProperties(RPTARG_DATE_TO);
            rtp.setString(RPTARG_DATE_TO[1], date_to);
        }
        // date_tz=
        if (!StringTools.isBlank(date_tz)) {
            rtp.removeProperties(RPTARG_DATE_TO);
            rtp.setString(RPTARG_DATE_TZ[1], date_tz);
        }
        // r_report=
        if (!StringTools.isBlank(r_report)) {
            rtp.removeProperties(RPTARG_REPORT);
            rtp.setString(RPTARG_REPORT[1], r_report);
        }
        // r_limit=
        if (!StringTools.isBlank(r_limit)) {
            rtp.removeProperties(RPTARG_LIMIT);
            rtp.setString(RPTARG_LIMIT[1], r_limit);
        }
        // r_limTyp=
        if (!StringTools.isBlank(r_limType)) {
            rtp.removeProperties(RPTARG_LIMIT_TYPE);
            rtp.setString(RPTARG_LIMIT_TYPE[1], r_limType);
        }
        // r_format=
        if (!StringTools.isBlank(r_format)) {
            rtp.removeProperties(RPTARG_FORMAT);
            rtp.setString(RPTARG_FORMAT[1], r_format);
        }
        Print.logInfo("Report RPT: " + rtp);

        /* remaining arguments */
        if (rtpEncode) {
            url.addArg(URLARG_RTP, rtp);
        } else {
            Map<Object,Object> props = rtp.getProperties();
            for (Object rtk : props.keySet()) {
                Object rtv = props.get(rtk);
                url.addArg((String)rtk, StringTools.trim(rtv));
            }
        }

        /* URL */
        return url;

    }
    
    // ------------------------------------------------------------------------
    // ------------------------------------------------------------------------

    public static void main(String args[])
    {
        RTConfig.setCommandLineArgs(args);
        
        if (RTConfig.hasProperty("urld")) {
            String urld = RTConfig.getString("urld","");
            URIArg rtpUrl = new URIArg(urld);
            URIArg decUrl = rtpUrl.rtpDecode(URLARG_RTP);
            Print.sysPrintln("Decoded URL: " + decUrl.toString());
            System.exit(0);
        }

        if (RTConfig.hasProperty("urle")) {
            String urle = RTConfig.getString("urle","");
            URIArg decUrl = new URIArg(urle);
            URIArg rtpUrl = decUrl.rtpEncode(URLARG_RTP, RPTARG_ACCOUNT, RPTARG_USER);
            Print.sysPrintln("Encoded URL: " + rtpUrl.toString());
            System.exit(0);
        }
        
        if (RTConfig.hasProperty("rpturl")) {
            String  baseURL   = StringTools.blankDefault(RTConfig.getString("rpturl",null), ".");
            String  accountID = RTConfig.getString("account","demo");
            String  userID    = RTConfig.getString("user"   ,null);
            String  deviceID  = RTConfig.getString("device" ,"demo");
            Account account   = null;
            Device  device    = null;
            try {
                account = Account.getAccount(accountID); // may throw DBException
                device  = Device.getDevice(account, deviceID, false); // may throw DBException
                if (device == null) {
                    Print.logError("Account/Device does not exist: " + accountID + "/" + deviceID);
                    System.exit(99);
                }
            } catch (DBException dbe) {
                Print.logError("Error getting Device: " + accountID + "/" + deviceID);
                dbe.printException();
                System.exit(99);
            }
            URIArg url = ReportURL.createEventDetailReportURL(
                userID, true/*inclPass*/,
                device, 0L/*timestamp*/, 
                baseURL);
            Print.sysPrintln("Report URL : " + url);
            URIArg decUrl = url.rtpDecode(URLARG_RTP);
            Print.sysPrintln("Decoded URL: " + decUrl.toString());

            System.exit(0);
        }

        String url       = RTConfig.getString("url"             ,"");
        String account   = RTConfig.getString(RPTARG_ACCOUNT    ,"");
        String user      = RTConfig.getString(RPTARG_USER       ,"");
        String encPass   = RTConfig.getString(RPTARG_ENCPASS    ,"");
        String device    = RTConfig.getString(RPTARG_DEVICE     ,"");
        String group     = RTConfig.getString(RPTARG_GROUP      ,"");
        String date_fr   = RTConfig.getString(RPTARG_DATE_FR    ,"");
        String date_to   = RTConfig.getString(RPTARG_DATE_TO    ,"");
        String date_tz   = RTConfig.getString(RPTARG_DATE_TZ    ,"");
        String r_report  = RTConfig.getString(RPTARG_REPORT     ,"");
        String r_limit   = RTConfig.getString(RPTARG_LIMIT      ,"");
        String r_limType = RTConfig.getString(RPTARG_LIMIT_TYPE ,"");
        String format    = RTConfig.getString(RPTARG_FORMAT     ,"");

        /* URL */
        URIArg rptURL = ReportURL.createReportURL(
            url, false,
            account, user, "",
            device, group,
            date_fr, date_to, date_tz,
            r_report, r_limit, r_limType,
            format);
        Print.logInfo("URL: " + rptURL);

    }

}

