'use strict';

var overlordService = angular.module('overlord.service', ['ngResource']);

overlordService.factory('IssueGrid', ['$resource', function ($resource) {
    return $resource('/api/Issues/Services/:date');
}]);

overlordService.factory('Voucher', ['$resource', function ($resource) {
    return $resource('/api/Voucher/:id',
        {id: '@VoucherID'}, {
            post: {method: 'POST', params: {post: true}},
            save: {
                method: 'POST',
                headers: {'Content-Type': undefined},
                transformRequest: function (orgData, headers) {
                    var data = angular.copy(orgData);

                    function dataURLtoBlob(dataURL) {
                        var re = /^data:([\w/\-\.]+);\w+,(.*)$/,
                            m = dataURL.match(re),
                            mimeString = m[1],
                            byteString = atob(m[2]),
                            ab = new ArrayBuffer(byteString.length),
                            dw = new DataView(ab),
                            i;

                        for (i = 0; i < byteString.length; i++) {
                            dw.setUint8(i, byteString.charCodeAt(i));
                        }
                        return new Blob([ab], {type: mimeString});
                    }

                    var fd = new FormData(),
                        files = [],
                        i;
                    for (i = data.Files.length - 1; i >= 0; i--) {
                        var item = data.Files[i];
                        if (!item.ID) {
                            fd.append('f' + i, dataURLtoBlob(item.Resized));
                            fd.append('t' + i, dataURLtoBlob(item.Thumbnail));
                            data.Files.splice(i, 1);
                        }
                    }
                    fd.append('model', angular.toJson(data));
                    return fd;
                }
            }
        });
}]);

overlordService.factory('Account', ['$resource', function ($resource) {
    return $resource('/api/Account/:id',
        {id: '@LedgerID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true},
            autocomplete: {method: 'GET', params: {term: ''}, isArray: true}
        });
}]);

overlordService.factory('Recipe', ['$resource', function ($resource) {
    return $resource('/api/Recipe/:id',
        {id: '@RecipeID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('Employee', ['$resource', function ($resource) {
    return $resource('/api/Employee/:id',
        {id: '@LedgerID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true},
            autocomplete: {method: 'GET', params: {term: ''}, isArray: true}
        });
}]);

overlordService.factory('Ledger', ['$resource', function ($resource) {
    return $resource('/api/Ledger/:id');
}]);

overlordService.factory('Reconcile', ['$resource', function ($resource) {
    return $resource('/api/Reconcile/:id', {id: '@Account.LedgerID'});
}]);

overlordService.factory('ProductLedger', ['$resource', function ($resource) {
    return $resource('/api/ProductLedger/:id');
}]);

overlordService.factory('CostCentre', ['$resource', function ($resource) {
    return $resource('/api/CostCentre/:id',
        {id: '@CostCentreID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('AccountType', ['$resource', function ($resource) {
    return $resource('/api/AccountTypes');
}]);

overlordService.factory('User', ['$resource', function ($resource) {
    return $resource('/api/User/:id',
        {id: '@UserID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true},
            names: {method: 'GET', params: {names: true}, isArray: true}
        });
}]);

overlordService.factory('Group', ['$resource', function ($resource) {
    return $resource('/api/Group/:id',
        {id: '@GroupID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('Client', ['$resource', function ($resource) {
    return $resource('/api/Client/:id',
        {id: '@ClientID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('Product', ['$resource', function ($resource) {
    return $resource('/api/Product/:id',
        {id: '@ProductID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true},
            autocomplete: {method: 'GET', params: {term: ''}, isArray: true}
        });
}]);

overlordService.factory('Batch', ['$resource', function ($resource) {
    return $resource('/api/Batch', {}, {
        autocomplete: {method: 'GET', params: {term: ''}, isArray: true}
    });
}]);

overlordService.factory('ProductGroup', ['$resource', function ($resource) {
    return $resource('/api/ProductGroup/:id',
        {id: '@ProductGroupID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('CostCentre', ['$resource', function ($resource) {
    return $resource('/api/CostCentre/:id',
        {id: '@CostCentreID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true}
        });
}]);

overlordService.factory('TrialBalance', ['$resource', function ($resource) {
    return $resource('/api/TrialBalance/:date');
}]);

overlordService.factory('ClosingStock', ['$resource', function ($resource) {
    return $resource('/api/ClosingStock/:date');
}]);

overlordService.factory('CashFlow', ['$resource', function ($resource) {
    return $resource('/api/CashFlow/:id');
}]);

overlordService.factory('Daybook', ['$resource', function ($resource) {
    return $resource('/api/Daybook');
}]);

overlordService.factory('Unposted', ['$resource', function ($resource) {
    return $resource('/api/Unposted');
}]);

overlordService.factory('ProfitLoss', ['$resource', function ($resource) {
    return $resource('/api/ProfitLoss');
}]);

overlordService.factory('StockMovement', ['$resource', function ($resource) {
    return $resource('/api/StockMovement');
}]);

overlordService.factory('BalanceSheet', ['$resource', function ($resource) {
    return $resource('/api/BalanceSheet/:date');
}]);

overlordService.factory('NetTransactions', ['$resource', function ($resource) {
    return $resource('/api/NetTransactions');
}]);

overlordService.factory('Purchases', ['$resource', function ($resource) {
    return $resource('/api/Purchases');
}]);

overlordService.factory('PurchaseEntries', ['$resource', function ($resource) {
    return $resource('/api/PurchaseEntries');
}]);

overlordService.factory('RawMaterialCost', ['$resource', function ($resource) {
    return $resource('/api/RawMaterialCost/:id');
}]);

overlordService.factory('Auth', ['$resource', function ($resource) {
    return $resource('/api/Auth', {}, {
        login: {method: 'GET', params: {list: true}, isArray: true}
    });
}]);

overlordService.factory('Attendance', ['$resource', function ($resource) {
    return $resource('/api/Attendance/:date', {date: '@Date'});
}]);

overlordService.factory('AttendanceTypes', ['$resource', function ($resource) {
    return $resource('/api/AttendanceTypes');
}]);

overlordService.factory('EmployeeAttendance', ['$resource', function ($resource) {
    return $resource('/api/EmployeeAttendance/:id', {id: '@Employee.LedgerID'});
}]);

overlordService.factory('Account', ['$resource', function ($resource) {
    return $resource('/api/Account/:id',
        {id: '@LedgerID'}, {
            query: {method: 'GET', params: {list: true}, isArray: true},
            autocomplete: {method: 'GET', params: {term: ''}, isArray: true}
        });
}]);
overlordService.factory('Message', ['$resource', function ($resource) {
    return $resource('/api/Message/:id',
        {id: '@ThreadID'}, {
            query: {method: 'GET', params: {list: true}}
        });
}]);

overlordService.factory('Tag', ['$resource', function ($resource) {
    return $resource('/api/Tags');
}]);

overlordService.factory('Tokenizer', ['$filter', function ($filter) {
    var parseFilterString = function (q, searchInfo) {
        // '[^']+'|"[^"]+"|[^ ]+ == Match a space delimited string or quoted string. ms for short
        // ((ms\s*:\s*ms)|ms) == Match string : Match string OR Match string
        var re = /((('[^']+'|"[^"]+"|[^\s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))|('[^']+'|"[^"]+"|[^\s]+))/g,
            comparator = searchInfo.comparator || {},
            sorter = searchInfo.sorter || {},
            flagger = searchInfo.flags || {},
            defaultKey = searchInfo.def,
            matches = [],
            sorting = [],
            flags = {},
            operators = {
                '<': function (a, b) {
                    return a < b;
                },
                '>': function (a, b) {
                    return a > b;
                },
                '<=': function (a, b) {
                    return a <= b;
                },
                '>=': function (a, b) {
                    return a >= b;
                },
                '==': function (a, b) {
                    return a === b;
                },
                '!=': function (a, b) {
                    return a !== b;
                }
            },
            comparators = {
                'text': function (obj, text) {
                    if (text.indexOf('!') === 0) {
                        return !obj.toLowerCase().includes(text.substr(1).toLowerCase());
                    } else {
                        return obj.toLowerCase().includes(text.toLowerCase());
                    }
                },
                'date': function (obj, text) {
                    var obDate = moment(obj, 'DD-MMM-YYYY'),
                        op = operatorLength(text),
                        operator = op ? text.substr(0, op) : '==',
                        textDate = op ? moment(text.substr(op).trim(), 'DD-MMM-YYYY') : moment(text, 'DD-MMM-YYYY');
                    return !obDate.isValid() || !textDate.isValid() || operators[operator](obDate.valueOf(), textDate.valueOf());
                },
                'boolean': function (obj, text) {
                    return obj === isTrue(text);
                },
                'numeric': function (obj, text) {
                    var op = operatorLength(text);
                    if (op) {
                        return operators[text.substr(0, op)](obj, Number(text.substr(op).trim()));
                    } else {
                        return obj.toString().indexOf(text) > -1;
                    }
                },
                'true': function () {
                    return true;
                }
            };


        function isTrue(value) {
            return !_.any(['f', 'fa', 'fal', 'fals', 'false', 'n', 'no', '0'], function (item) {
                return item === value;
            });
        }

        function is(ch, chars) {
            return chars.indexOf(ch) !== -1;
        }

        function pushObject(comparator, value) {
            return {'Col': comparator.Col, 'Comparator': comparators[comparator.Comparator], 'Value': value};
        }

        function operatorLength(operator) {
            var i,
                ops = ['<=', '<', '>=', '>', '==', '!='];
            for (i = 0; i < ops.length; i++) {
                if (operator.indexOf(ops[i]) === 0) {
                    return ops[i].length;
                }
            }
            return 0;
        }

        _(q.match(re))
            .reduce(function (accumulator, item) {
                var key,
                    value;
                if (item.indexOf(':') === -1) {
                    key = '';
                    value = item;
                } else {
                    key = item.substr(0, item.indexOf(':')).trim();
                    value = item.substr(item.indexOf(':') + 1, item.length).trim();
                }
                if (key.indexOf("'") !== -1 || key.indexOf('"') !== -1) {
                    key = key.substring(1, key.length - 1).trim();
                }
                if (value.indexOf("'") !== -1 || value.indexOf('"') !== -1) {
                    value = value.substring(1, value.length - 1).trim();
                }
                if (value !== '') {
                    accumulator.push({Key: key, Value: value});
                }
                return accumulator;
            }, [])
            .forEach(function (item) {
                var key = item.Key,
                    value = item.Value;

                if (key === '' && value.length > 1 && is(value.charAt(0), '+-') && value.substr(1) in sorter) {
                    sorting.push(value.charAt(0) + sorter[value.substr(1).toLowerCase()]);
                } else {
                    key = key || defaultKey;
                    if (key in comparator) {
                        matches.push(pushObject(comparator[key], value));
                    } else if (key in flagger) {
                        flags[key] = value;
                    }
                }
            });
        return {'q': matches, 'o': sorting, 'f': flags};
    };
    var doFilter = _.memoize(function (q, array, matches) {
        var filterExpressions = matches.q,
            sortPredicates = matches.o,
            filterCount = filterExpressions.length;
        var arrayCopy = filterCount === 0 ? array : _.filter(array, function (item) {
            return _.every(filterExpressions, function (expression) {
                return expression.Comparator(item[expression.Col], expression.Value);
            });
        });
        arrayCopy = $filter('orderBy')(arrayCopy, sortPredicates);
        return arrayCopy;
    });

    return {parseFilterString: parseFilterString, doFilter: doFilter};
}]);

overlordService.factory("ReaderPromise", ["$q", function ($q) {
    var onLoad = function (reader, deferred) {
        return function () {
            deferred.resolve(reader.result);
        };
    };

    var onError = function (reader, deferred) {
        return function () {
            deferred.reject(reader.result);
        };
    };

    var onProgress = function (reader, scope) {
        return function (event) {
            scope.$emit("fileProgress",
                {
                    total: event.total,
                    loaded: event.loaded
                });
        };
    };

    var getReader = function (deferred, scope) {
        var reader = new FileReader();
        reader.onload = onLoad(reader, deferred);
        reader.onerror = onError(reader, deferred, scope);
        if (!angular.isUndefined(scope)) {
            reader.onprogress = onProgress(reader, scope);
        }
        return reader;
    };

    var readAsDataURL = function (file, scope) {
        var deferred = $q.defer(),
            reader = getReader(deferred, scope);
        reader.readAsDataURL(file);
        return deferred.promise;
    };

    return {
        readAsDataURL: readAsDataURL
    };
}]);

overlordService.factory("uploadedImageResizer", ["$q", "ReaderPromise", function ($q, ReaderPromise) {
    function resizeImage(file, MAX_WIDTH, MAX_HEIGHT) {
        var image = file,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext("2d"),
            img = document.createElement("img"),
            ratio = 1;

        img.src = image;
        var width = img.width,
            height = img.height;

        if (width > MAX_WIDTH) {
            ratio = MAX_WIDTH / width;
        }
        if (height > MAX_HEIGHT && MAX_HEIGHT / height < ratio) {
            ratio = MAX_HEIGHT / height;
        }
        if (ratio === 1) {
            return file;
        }

        width *= ratio;
        height *= ratio;

        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0, width, height);
        return canvas.toDataURL("image/jpeg", 0.95);
    }

    var ProcessUpload = function (uploadedFile, oldFiles) {
        var file = {File: uploadedFile.file},
            exists = _.any(oldFiles, function (value) {
                return angular.equals(value.File, file.File);
            });

        if (exists) {
            return;
        }
        ReaderPromise.readAsDataURL(uploadedFile.file).then(function (result) {
            file.Original = result;
            file.Thumbnail = resizeImage(result, 100, 150);
            file.Resized = resizeImage(result, 825, 1170);
        });
        oldFiles.push(file);
    };


    return ProcessUpload;
}]);
