﻿dojo.declare("ParcelSearch", null,
{
    _params: null,
    _map: null,
    _serviceUrl: null,
    _layerId: null,
    _apnField: null,
    _addressField: null,
    _ownerFields: null,
    _outFields: null,
    _token: null,
    _findTask: null,
    _findParams: null,
    _queryTask: null,
    _queryParams: null,
    _addrFindTask: null,
    _addrFindParams: null,
    _msgDiv: null,
    _controlId: null,
    _addressTextBoxId: null,
    _fieldAliases: null,
    _watermarkApn: null,
    _initExtent: null,

    constructor: function (params) {
        this._params = params;
        this._map = params.mapObj;
        this._serviceUrl = params.serviceUrl;
        this._layerId = params.layerId;
        this._apnField = params.apnField;
        this._ownerFields = params.ownerFields;
        this._outFields = params.outFields;
        this._token = params.token;
        this._controlId = params.controlId;
        this._watermarkApn = dojo.byId(this._controlId + "_parcelSearchInput").value;
        dojo.hitch(this, this.findParcels);
        this.getAliases();
        this._initExtent = this._map.extent;
        this._msgDiv = $('#parcelSearchMsg');

        // check for startup apn
        var cmd = this.qString("cmd");
        if (cmd && cmd === "findbyapn") {
            var apn = this.qString("parcel");
            if (apn && apn != "") {
                $("#" + this._controlId + "_parcelSearchInput").val(apn);
                this.findParcels();
            }
        }
    },

    getAliases: function () {
        var url = this._serviceUrl + "/" + this._layerId + "?f=json";
        if (this._token && this._token != "")
            url += "?token=" + this._token;
        var args = { url: this._serviceUrl + "/" + this._layerId,
            content: {
                f: "json",
                token: this._token
            },
            callbackParamName: "callback",
            load: dojo.hitch(this, this.aliasesCallback),
            error: function (err) {
                // Just use field names instead of aliases
            }
        }
        dojo.io.script.get(args);
    },

    aliasesCallback: function (data) {
        var fields = data.fields;
        var aliases = {};

        for (var field, i = -1; field = fields[++i]; ) {
            aliases[field.name] = field.alias;
        }
        this._fieldAliases = aliases;
    },

    findAddress: function () {
        this.isBusy(true);
        this.initTask();
        var addr = $.trim($('#' + this._controlId + '_parcelSearchAddressInput').val());
        if (addr != "") {
            this.hideMsg();
            switch (this._params.addressSearchType) {
                case 'Geocode':
                    // 1) geocode the address. 2) Use x/y to find parcel and pass to callback. 3) If not found at x/y, may try to use standardized address to find parcel?
                    googleGeocoder.findAddress(addr, dojo.hitch(this, this.geocodeCallback), dojo.hitch(this, this.addressError));
                    break;
                case 'ParcelLayer':
                    // search parcel layer for address
                    this._findParams.searchText = addr;
                    this._findParams.searchFields = [this._params.addressSearchField];
                    this._findTask.execute(this._findParams, dojo.hitch(this, this.findCallback), dojo.hitch(this, this.findError));
                    break;
                case 'PointLayer':
                    // search an address points layer
                    this._addrFindParams.searchText = addr;
                    this._addrFindTask.execute(this._addrFindParams, dojo.hitch(this, this.addressPointsCallback), dojo.hitch(this, this.findError));
                    break;
                default:
                    alert('No addressSearchType defined');
                    this.isBusy(false);
                    break;
            }
        }
        else {
            this.isBusy(false);
            this.showMsg("Please enter an address to find.");
        }
    },

    geocodeCallback: function (result) {
        // geocoding: if any results, use the first one within the map's initial extent.
        var foundPt = null;
        var foundAddress = false;
        var pt, res, gLoc, addrType;
        if (result && result.results) {
            for (var i = 0, il = result.results.length; i < il; i++) {
                res = result.results[i];
                gLoc = res.geometry.location;
                pt = new esri.geometry.Point(gLoc.lng, gLoc.lat, new esri.SpatialReference({ wkid: 4326 }));
                pt = esri.geometry.geographicToWebMercator(pt);
                // Only use points in the project area
                if (this._initExtent.contains(pt)) {
                    foundPt = pt;
                    // check if it's a street address or just city etc.
                    for (var t = 0, tl = res.types.length; t < tl; t++) {
                        if (res.types[i] == "street_address") {
                            //this.locateParcels([foundPt]);
                            foundAddress = true;
                            break;
                        }
                    }
                }
            }
        }

        if (foundPt) {
            if (foundAddress)
                this.locateParcels([foundPt]);
            else {
                // Only found the street/city/etc. - zoom near it
                this.isBusy(false);
                this.showMsg("Street address not found, but found a location nearby. You can use the Identify tool to select a parcel.");
                this._map.centerAndZoom(foundPt, 17);
            }
        }
        else {
            // TODO: try finding parcel by address?
            this.isBusy(false);
            this.showMsg("Address could not be located in the website area. Please check address and/or include city/state or zip code.");
            parcelResults.clear();
        }
    },

    addressError: function (err) {
        this.isBusy(false);
        this.showMsg("Error occurred finding the address. Try including city/state or zip. If error continues, contact Support.");
    },

    addressPointsCallback: function (results) {
        if (results && results.length > 0) {
            // get address points and find parcels with them
            var addrPts = [];
            for (var fr, i = -1; fr = results[++i]; ) {
                addrPts.push(fr.feature.geometry);
            }
            this.locateParcels(addrPts);
        }
        else {
            this.isBusy(false);
            this.showMsg("No matching addresses found. Tip: try searching with partial address, such as just the street name or number.");
        }
    },

    locateParcels: function (points) {
        // Finds parcel at point(s) (from geocoding or address-points)
        this.hideMsg();

        var addrMP = new esri.geometry.Multipoint(points[0].spatialReference);
        for (var pt, i = -1; pt = points[++i]; ) {
            addrMP.addPoint(pt);
        }

        this._queryParams.where = "";
        this._queryParams.geometry = points[0]; // addrMP;
        this._queryTask.execute(this._queryParams, dojo.hitch(this, this.queryCallback), dojo.hitch(this, this.findError));
    },

    queryCallback: function (results) {
        if (results.features && results.features.length > 0) {
            // Pass to parcel results control
            dojo.hitch(parcelResults, parcelResults.displayResults);
            parcelResults.displayResults(results, false, this._layerId);
        }
        else {
            parcelResults.clear();
            this.isBusy(false);

            // no parcel - zoom to the area if it was an address query
            if (this._queryParams.geometry) {
                this._map.centerAndZoom(this._queryParams.geometry, 17);
                this.showMsg("Address was located, but no matching parcel found. You can use the Identify tool to select a parcel on the map.");
            }
            else
                this.showMsg("No parcels with this owner name found.");
        }
    },

    findParcels: function () {
        // Finds parcel by APN
        this.initTask();
        this.hideMsg();
        var searchVal = "";
        searchVal = dojo.byId(this._controlId + "_parcelSearchInput").value;

        if (searchVal != "" && searchVal != this._watermarkApn) {
            this.isBusy(true);
            this._findParams.searchText = searchVal;
            this._findParams.searchFields = [this._apnField];
            this._findTask.execute(this._findParams, dojo.hitch(this, this.findCallback), dojo.hitch(this, this.findError));
            //this._queryParams.text = searchVal;
            //this._queryTask.execute(this._queryParams, dojo.hitch(this, this.queryCallback), dojo.hitch(this, this.queryError));
        }
        else {
            this.showMsg("Please enter search text.");
        }
    },

    findCallback: function (results) {
        // results from either findParcels or findAddress
        if (results && results.length > 0) {
            // Parcel results expects FeatureSet
            var fSet = new esri.tasks.FeatureSet();
            for (var findRes, i = -1; findRes = results[++i]; ) {
                if (i == 0) {
                    fSet.displayFieldName = findRes.displayFieldName;
                    fSet.geometryType = findRes.geometryType;
                    fSet.spatialReference = findRes.feature.geometry.spatialReference;
                }
                fSet.features.push(findRes.feature);
                // TODO?: get field aliases; 
            }
            fSet.fieldAliases = this._fieldAliases;

            // Pass to parcel results control
            dojo.hitch(parcelResults, parcelResults.displayResults);
            parcelResults.displayResults(fSet, false, this._layerId, true);
        }
        else {
            this.isBusy(false);
            this.showMsg("No matching parcels found.");
            parcelResults.clear();
        }
    },

    findError: function (err) {
        this.isBusy(false);
        var msg = err.code + ":" + err.message;
        if (err.details && err.details.length > 0)
            msg += " " + err.details[0];
        this.showMsg("Error occurred locating parcel. If problem persists, please contact Support.");
        parcelResults.clear();
    },

    findOwner: function () {
        this.initTask();
        this.hideMsg();

        var ownerVal = $.trim($('#' + this._controlId + '_parcelSearchOwnerInput').val()).toLowerCase();
        if (ownerVal != "") {
            this.isBusy(true);
            // handle multiple owner fields
            var ownerFieldArr = this._ownerFields.split(',');
            var where = "";
            for (var i = 0; i < ownerFieldArr.length; i++) {
                if (where.length > 0)
                    where += " OR ";
                where += "LOWER(" + ownerFieldArr[i] + ") like '" + ownerVal + "%'";
            }

            this._queryParams.where = where;
            this._queryParams.geometry = null;
            this._queryTask.execute(this._queryParams, dojo.hitch(this, this.queryCallback), dojo.hitch(this, this.findError));
        }
        else
            this.showMsg("Please enter an owner value to search for.");
    },

    initTask: function () {
        if (!this._queryTask || !this._findTask) {
            var tokenStr = (this._token && this._token != "") ? "?token=" + this._token : "";
            var url = this._serviceUrl + tokenStr;
            var layerUrl = this._serviceUrl + "/" + this._layerId + tokenStr;

            // find task for apn search
            this._findTask = new esri.tasks.FindTask(url);
            this._findParams = new esri.tasks.FindParameters();
            this._findParams.contains = true;
            this._findParams.layerIds = [this._layerId];
            //this._findParams.searchFields = [this._apnField];
            this._findParams.returnGeometry = true;

            // query task used when finding owner
            this._queryTask = new esri.tasks.QueryTask(layerUrl);
            this._queryParams = new esri.tasks.Query();
            var outfields = ['*'];
            if (this._outFields && this._outFields != "")
                outfields = this._outFields.split(',');
            this._queryParams.outFields = outfields;
            this._queryParams.returnGeometry = true;

            // query task for address search if by address points
            if (this._params.addressSearchType === 'PointLayer') {

                var addrUrl = this._serviceUrl + tokenStr;

                this._addrFindTask = new esri.tasks.FindTask(addrUrl);
                this._addrFindParams = new esri.tasks.FindParameters();
                this._addrFindParams.contains = true;
                this._addrFindParams.layerIds = [this._params.addressSearchLayerId];
                this._addrFindParams.searchFields = [this._params.addressSearchField];
                this._addrFindParams.returnGeometry = true;
            }
        }
    },

    showMsg: function (txt) {
        this._msgDiv.html(txt);
        this._msgDiv.css('display', '');
    },

    hideMsg: function () {
        this._msgDiv.css('display', 'none');
    },

    isBusy: function (busy) {
        if (busy)
            $('#imgMapLoading').show();  // HACK: img on main map div
        else
            $('#imgMapLoading').hide();
    },

    qString: function (param) {
        var qs = window.location.search.substring(1);
        var qsItems = qs.split('&');
        var qsPr;
        for (i = 0; i < qsItems.length; i++) {
            qsPr = qsItems[i].split('=');
            if (qsPr[0].toLowerCase() == param.toLowerCase())
                return qsPr[1];
        }
        return null;
    }
});
