/*
 * Tencent is pleased to support the open source community by making Tinker available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * 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.
 */

package com.tencent.tinker.build.decoder;

import com.tencent.tinker.build.info.InfoWriter;
import com.tencent.tinker.build.patch.Configuration;
import com.tencent.tinker.build.util.DiffFactory;
import com.tencent.tinker.build.util.FileOperation;
import com.tencent.tinker.build.util.Logger;
import com.tencent.tinker.build.util.MD5;
import com.tencent.tinker.build.util.TinkerPatchException;
import com.tencent.tinker.build.util.Utils;

import java.io.File;
import java.io.IOException;

/**
 * Created by zhangshaowen on 16/2/27.
 */
public class SoDiffDecoder extends BaseDecoder {
    private final InfoWriter logWriter;
    private final InfoWriter metaWriter;

    public SoDiffDecoder(Configuration config, String metaPath, String logPath) throws IOException {
        super(config);

        if (metaPath != null) {
            metaWriter = new InfoWriter(config, config.mTempResultDir + File.separator + metaPath);
        } else {
            metaWriter = null;
        }

        if (logPath != null) {
            logWriter = new InfoWriter(config, config.mOutFolder + File.separator + logPath);
        } else {
            logWriter = null;
        }
    }

    @Override
    public void clean() {
        logWriter.close();
        metaWriter.close();
    }

    @Override
    public boolean patch(File oldFile, File newFile) throws IOException, TinkerPatchException {
        //first of all, we should check input files
        if (newFile == null || !newFile.exists()) {
            return false;
        }
        //new add file
        String newMd5 = MD5.getMD5(newFile);
        File diffFile = getOutputPath(newFile).toFile();

        if (oldFile == null || !oldFile.exists()) {
            FileOperation.copyFileUsingStream(newFile, diffFile);
            writeLogFiles(newFile, null, null, newMd5);
            return true;
        }

        //both file length is 0
        if (oldFile.length() == 0 && newFile.length() == 0) {
            return false;
        }
        if (oldFile.length() == 0 || newFile.length() == 0) {
            FileOperation.copyFileUsingStream(newFile, diffFile);
            writeLogFiles(newFile, null, null, newMd5);
            return true;
        }

        //new add file
        String oldMd5 = MD5.getMD5(oldFile);

        if (oldMd5.equals(newMd5)) {
            return false;
        }

        if (!diffFile.getParentFile().exists()) {
            diffFile.getParentFile().mkdirs();
        }

        diffFile(oldFile, newFile, diffFile);

        if (Utils.checkBsDiffFileSize(diffFile, newFile)) {
            writeLogFiles(newFile, oldFile, diffFile, newMd5);
        } else {
            FileOperation.copyFileUsingStream(newFile, diffFile);
            writeLogFiles(newFile, null, null, newMd5);
        }
        return true;
    }

    @Override
    public void onAllPatchesStart() throws IOException, TinkerPatchException {

    }

    @Override
    public void onAllPatchesEnd() throws IOException, TinkerPatchException {

    }

    protected void writeLogFiles(File newFile, File oldFile, File bsDiff, String newMd5) throws IOException {
        if (metaWriter == null && logWriter == null) {
            return;
        }
        String parentRelative = getParentRelativePathStringToNewFile(newFile);
        String relative = getRelativePathStringToNewFile(newFile);

        if (metaWriter != null) {
            String fileName = newFile.getName();

            String meta;
            if (bsDiff == null || oldFile == null) {
                meta = fileName + "," + parentRelative + "," + newMd5 + "," + 0 + "," + 0;
            } else {
                String oldCrc = FileOperation.getZipEntryCrc(config.mOldApkFile, relative);
                if (oldCrc == null || oldCrc.equals("0")) {
                    throw new TinkerPatchException(
                        String.format("can't find zipEntry %s from old apk file %s", relative, config.mOldApkFile.getPath())
                    );
                }
                meta = fileName + "," + parentRelative + "," + newMd5 + "," + oldCrc + "," + MD5.getMD5(bsDiff);
            }
            Logger.d("BsDiffDecoder:write meta file data: %s", meta);
            metaWriter.writeLineToInfoFile(meta);
        }

        if (logWriter != null) {
            String log = relative + ", oldSize=" + FileOperation.getFileSizes(oldFile) + ", newSize="
                + FileOperation.getFileSizes(newFile) + ", diffSize=" + FileOperation.getFileSizes(bsDiff);

            logWriter.writeLineToInfoFile(log);
        }
    }

    private void diffFile(File oldFile, File newFile, File diffFile) throws IOException {
        DiffFactory.diffFile(config, oldFile, newFile, diffFile);
    }
}
