/**
 * Copyright 2019 Huawei Technologies Co.,Ltd.
 * 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.
 */

package com.obs.services.internal.task;

import java.util.Date;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.obs.services.internal.utils.ServiceUtils;
import com.obs.services.model.ProgressStatus;
import com.obs.services.model.UploadProgressStatus;

public class UploadTaskProgressStatus implements UploadProgressStatus {

    private final long progressInterval;
    private final Date startDate;
    private ConcurrentHashMap<String, ProgressStatus> taskTable = new ConcurrentHashMap<String, ProgressStatus>();
    private AtomicLong totalSize = new AtomicLong();
    private AtomicLong totalMilliseconds = new AtomicLong();
    private AtomicLong endingTaskSize = new AtomicLong();
    private AtomicInteger execTaskNum = new AtomicInteger();
    private AtomicInteger succeedTaskNum = new AtomicInteger();
    private AtomicInteger failTaskNum = new AtomicInteger();
    private AtomicInteger totalTaskNum = new AtomicInteger();
    private AtomicLong taskTagSize = new AtomicLong();

    public UploadTaskProgressStatus(long progressInterval, Date startDate) {
        this.progressInterval = progressInterval;
        this.startDate = ServiceUtils.cloneDateIgnoreNull(startDate);
    }

    public void execTaskIncrement() {
        execTaskNum.incrementAndGet();
    }

    public void succeedTaskIncrement() {
        succeedTaskNum.incrementAndGet();
    }

    public void failTaskIncrement() {
        failTaskNum.incrementAndGet();
    }

    public void setTaskTagSize(long taskTagSize) {
        this.taskTagSize = new AtomicLong(taskTagSize);
    }

    public long getTaskTagSize() {
        return taskTagSize.get();
    }

    public void setTotalTaskNum(int totalNum) {
        this.totalTaskNum.set(totalNum);
    }

    public boolean isRefreshprogress() {
        if (this.progressInterval <= 0) {
            return false;
        }

        long transferredSize = this.getTransferredSize();
        long taskTagSize = this.getTaskTagSize();
        if (transferredSize - taskTagSize >= progressInterval) {
            this.setTaskTagSize(transferredSize);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public int getExecPercentage() {
        if (totalTaskNum.get() <= 0) {
            return -1;
        } else {
            return execTaskNum.get() * 100 / totalTaskNum.get();
        }
    }

    @Override
    public int getTotalTaskNum() {
        return totalTaskNum.get();
    }

    @Override
    public int getExecTaskNum() {
        return execTaskNum.get();
    }

    @Override
    public int getSucceedTaskNum() {
        return succeedTaskNum.get();
    }

    @Override
    public int getFailTaskNum() {
        return failTaskNum.get();
    }

    @Override
    public long getTotalSize() {
        // 任务未加载完毕时，返回-1
        if (getTotalTaskNum() <= 0) {
            return -1L;
        } else {
            return totalSize.get();
        }
    }

    @Override
    public long getTransferredSize() {
        long transferredSize = this.endingTaskSize.get();
        ConcurrentHashMap<String, ProgressStatus> taskStatusTable = new ConcurrentHashMap<String, ProgressStatus>(
                this.taskTable);
        for (Entry<String, ProgressStatus> entry : taskStatusTable.entrySet()) {
            transferredSize += entry.getValue().getTransferredBytes();
        }
        return transferredSize;
    }

    @Override
    public double getInstantaneousSpeed() {
        if (this.taskTable != null) {
            long instantaneousSpeed = 0;
            for (Entry<String, ProgressStatus> entry : this.taskTable.entrySet()) {
                instantaneousSpeed += entry.getValue().getInstantaneousSpeed();
            }
            return instantaneousSpeed;
        } else {
            return -1d;
        }
    }

    @Override
    public double getAverageSpeed() {
        if (this.totalMilliseconds.get() <= 0) {
            return -1d;
        }
        return this.getTransferredSize() * 1000.0d / this.totalMilliseconds.get();
    }

    @Override
    public ConcurrentHashMap<String, ProgressStatus> getTaskTable() {
        ConcurrentHashMap<String, ProgressStatus> taskStatusTable = new ConcurrentHashMap<String, ProgressStatus>(
                this.taskTable);
        return taskStatusTable;
    }

    @Override
    public ProgressStatus getTaskStatus(String key) {
        return this.taskTable.get(key);
    }

    public void setTotalSize(long totalSize) {
        this.totalSize = new AtomicLong(totalSize);
    }

    public void addTotalSize(long bytes) {
        this.totalSize.addAndGet(bytes);
    }

    public void setTaskTable(ConcurrentHashMap<String, ProgressStatus> taskTable) {
        this.taskTable = taskTable;
    }

    public void putTaskTable(String key, ProgressStatus status) {
        this.taskTable.put(key, status);
    }

    public void removeTaskTable(String key) {
        if (null == this.taskTable) {
            return;
        }
        this.taskTable.remove(key);
    }

    public long getTotalMilliseconds() {
        return totalMilliseconds.get();
    }

    public void setTotalMilliseconds(long totalMilliseconds) {
        this.totalMilliseconds = new AtomicLong(totalMilliseconds);
    }

    public long getEndingTaskSize() {
        return endingTaskSize.get();
    }

    public void setEndingTaskSize(long endingTaskSize) {
        this.endingTaskSize = new AtomicLong(endingTaskSize);
    }

    public void addEndingTaskSize(long bytes) {
        this.endingTaskSize.addAndGet(bytes);
    }

    public Date getStartDate() {
        return ServiceUtils.cloneDateIgnoreNull(this.startDate);
    }

}
