/**
 * release.js
 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * DongHee Yang <donghee.yang@samsung.com>
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jiil Hyoun <jiil.hyoun@samsung.com>
 * Jonghwan Park <iwin100.park@samsung.com>
 * Kitae Kim <kt920.kim@samsung.com>
 *
 * 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.
 *
 * Contributors:
 * - S-Core Co., Ltd
**/

var fs = require('fs');
var extfs = require('fs-extra');
var path = require('path');
var async = require('async');
var synchronized = require('synchronized');

var dfs = require('../../plugins/dibs.dist-fs/dist-fs.js');

module.exports.create = createRelease;
module.exports.remove = removeRelease;
module.exports.update = updateRelease;
module.exports.load = loadReleases;


function TizenRelease(name) {
    this.name = name;
    this.type = 'official';
    this.creationTime = null;
    this.modificationTime = null;
    this.path = null;
    this.snapshotName = null;
    this.releaseNote = null;
    this.releaseFiles = null;
}


function createRelease(releaseName, snapshotName, distPath, opts, callback) {

    var releasePath = 'releases/' + releaseName;
    var releaseDirPath = path.join(distPath, 'releases', releaseName);
    var releaseType = opts.type ? opts.type : 'official';
    var releaseNote = opts.releaseNote ? opts.releaseNote : '';

    // TODO option 'releaseFiles' check

    synchronized(distPath, function (done) {
        async.waterfall([
            function (cb) {
                createReleaseDirectory(releaseDirPath, cb);
            },
            // copy release files
            function (cb) {
                registerReleaseFiles(opts.releaseFiles, releaseDirPath, opts, cb);
            },
            // save release info
            function (resultFiles, cb) {
                var release = new TizenRelease(releaseName);
                release.type = releaseType;
                release.creationTime = new Date();
                release.modificationTime = release.creationTime;
                release.path = releasePath;
                release.snapshotName = snapshotName;
                release.releaseNote = releaseNote;
                release.releaseFiles = resultFiles;

                putToReleaseInfo(release, distPath, cb);
            }
        ],
        function (err, release) {
            if (err) {
                deleteReleaseDirectory(releaseDirPath, function (err1) {
                    done(err, null);
                });
            } else {
                done(err, release);
            }
        });
    },
    function (err, release) {
        callback(err, release);
    });
}


function createReleaseDirectory(releaseDirPath, callback) {
    async.series([
        // check base directory
        function (cb) {
            var baseDirPath = path.dirname(releaseDirPath);
            fs.exists(baseDirPath, function (exists) {
                if (!exists) {
                    extfs.mkdirp(baseDirPath, cb);
                } else {
                    cb(null);
                }
            });
        },
        // check already exists
        function (cb) {
            fs.exists(releaseDirPath, function (exists) {
                if (exists) {
                    extfs.remove(releaseDirPath, cb);
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            extfs.mkdirp(releaseDirPath, cb);
        }], function (err) {
        callback(err);
    });
}


function registerReleaseFiles(newReleaseFiles, releaseDirPath, opts, callback) {
    var result = [];

    if (!newReleaseFiles) {
        newReleaseFiles = [];
    }

    async.eachSeries(newReleaseFiles,
        function (relFileInfo, cb) {
            registerReleaseFile(relFileInfo, releaseDirPath, function (err, info) {
                if (!err) {
                    result.push(info);
                }
                cb(err);
            });
        },
        function (err) {
            callback(err, result);
        });
}


function registerReleaseFile(fileInfo, releaseDirPath, callback) {

    var relFileName = path.basename(fileInfo.path);
    var tpath = path.join(releaseDirPath, relFileName);

    // get file from remote
    dfs.getFile(tpath, fileInfo.path, function (err) {
        if (!err) {
            var dfsFileInfo = dfs.getFileInfo(fileInfo.path);
            var relFilePath = 'releases/' + path.basename(releaseDirPath) +
                '/' + relFileName;
            var relFileInfo = {
                name: relFileName,
                path: relFilePath,
                size: dfsFileInfo.size,
                checksum: dfsFileInfo.checksum
            };
            relFileInfo.category = fileInfo.category ? fileInfo.category : null;
            relFileInfo.os = fileInfo.os ? fileInfo.os : null;
            relFileInfo.description = fileInfo.description ? fileInfo.description : '';
            relFileInfo.repoUrl = fileInfo.repoUrl ? fileInfo.repoUrl : null;
            relFileInfo.includeList = fileInfo.includeList ? fileInfo.includeList : null;
            relFileInfo.excludeList = fileInfo.excludeList ? fileInfo.excludeList : null;

            callback(err, relFileInfo);
        } else {
            callback(err, null);
        }
    });
}


function putToReleaseInfo(release, distPath, callback) {
    var infoFilePath = path.join(distPath, 'releases.info');

    async.waterfall([
        // load releaseInfo if if exists
        function (cb) {
            fs.exists(infoFilePath, function (exists) {
                if (exists) {
                    extfs.readJson(infoFilePath, cb);
                } else {
                    cb(null, []);
                }
            });
        },
        function (releaseInfo, cb) {
            var matched = releaseInfo.filter(function (rel) {
                return rel.name === release.name;
            });
            if (matched.length === 0) {
                releaseInfo.push(release);
            } else {
                releaseInfo.splice(releaseInfo.indexOf(matched[0]), 1, release);
            }
            extfs.outputJson(infoFilePath, releaseInfo, cb);
        }], function (err) {
        callback(err, release);
    });
}


function removeRelease(release, distPath, opts, callback) {
    var releaseDirPath = path.join(distPath, 'releases', release.name);

    synchronized(distPath, function (done) {
        async.waterfall([
            // update release info
            function (cb) {
                removeFromReleaseInfo(release.name, distPath, cb);
            },
            // delete release directory
            function (cb) {
                deleteReleaseDirectory(releaseDirPath, cb);
            }], function (err) {
            done(err, release);
        });
    },
        function (err, release) {
            callback(err, release);
        });
}


function removeFromReleaseInfo(releaseName, distPath, callback) {
    var infoFilePath = path.join(distPath, 'releases.info');

    async.waterfall([
        // load releaseInfo if if exists
        function (cb) {
            fs.exists(infoFilePath, function (exists) {
                if (exists) {
                    extfs.readJson(infoFilePath, cb);
                } else {
                    cb(null, []);
                }
            });
        },
        function (releaseInfo, cb) {
            var matched = releaseInfo.filter(function (rel) {
                return rel.name === releaseName;
            });
            if (matched.length === 1) {
                releaseInfo.splice(releaseInfo.indexOf(matched[0]), 1);
            }
            extfs.outputJson(infoFilePath, releaseInfo, cb);
        }], function (err) {
        callback(err);
    });
}


function deleteReleaseDirectory(releaseDirPath, callback) {

    fs.exists(releaseDirPath, function (exists) {
        if (exists) {
            extfs.remove(releaseDirPath, callback);
        } else {
            callback(null);
        }
    });
}


function updateRelease(release, distPath, opts, callback) {
    var releaseDirPath = path.join(distPath, 'releases', release.name);

    synchronized(distPath, function (done) {
        async.waterfall([
            // copy release files
            function (cb) {
                registerReleaseFiles(opts.releaseFiles, releaseDirPath, opts, cb);
            },
            // delete release files
            function (resultFiles, cb) {
                // update
                resultFiles.forEach(function (resultFile) {
                    var matched = release.releaseFiles.filter(function (relFile) {
                        return relFile.name === resultFile.name;
                    });

                    if (matched.length === 0) {
                        release.releaseFiles.push(resultFile);
                    } else {
                        release.releaseFiles.splice(
                            release.releaseFiles.indexOf(matched[0]), 1,
                            resultFile);
                    }
                });

                // delete
                deleteUnnecessaryFiles(release.releaseFiles, releaseDirPath, cb);
            },
            // save release info
            function (cb) {
                release.modificationTime = new Date();

                putToReleaseInfo(release, distPath, cb);
            }], function (err, release) {
            done(err, release);
        });
    },
        function (err, release) {
            callback(err, release);
        });
}


function deleteUnnecessaryFiles(releaseFiles, releaseDirPath, callback) {
    if (!releaseFiles) {
        releaseFiles = [];
    }

    fs.readdir(releaseDirPath, function (err, files) {
        if (err) {
            callback(err);
        } else {
            async.eachSeries(files,
                function (fileName, cb) {
                    // select the files which are not included
                    var matched = releaseFiles.filter(function (f) {
                        return f.name === fileName;
                    });
                    if (matched.length === 0) {
                        var filePath = path.join(releaseDirPath, fileName);
                        extfs.remove(filePath, cb);
                    } else {
                        cb(null);
                    }
                },
                function (err) {
                    callback(err);
                });
        }
    });
}


function loadReleases(distPath, callback) {
    var releases = null;
    var infoFilePath = path.join(distPath, 'releases.info');

    async.waterfall([
        // load releaseInfo if if exists
        function (cb) {
            fs.exists(infoFilePath, function (exists) {
                if (exists) {
                    extfs.readJson(infoFilePath, cb);
                } else {
                    callback(null, []);
                }
            });
        },
        function (releaseInfo, cb) {
            releases = releaseInfo;
            extfs.outputJson(infoFilePath, releaseInfo, cb);
        }], function (err) {
        callback(err, releases);
    });
}
