/*
 * Copyright (C) 2016 The Qt Company Ltd.
 * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
 * Copyright (c) 2018-2019 TOYOTA MOTOR CORPORATION
 *
 * 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.
 */

#include "serverappmodel.h"
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include "afm_user_daemon_proxy.h"
#include "httpclient.h"

#include "hmi-debug.h"

extern org::AGL::afm::user* afm_user_daemon_proxy;

ServerAppModel::ServerAppModel(QObject* parent) : QAbstractListModel(parent) {
  // this->getAppPage(0, PAGE_SIZE);
  connect(afm_user_daemon_proxy, &org::AGL::afm::user::changed, this,
          &ServerAppModel::appChanged);
}

ServerAppModel::~ServerAppModel() {}

int ServerAppModel::rowCount(const QModelIndex& parent) const {
  if (parent.isValid())
    return 0;

  return this->applist.count();
}

QVariant ServerAppModel::data(const QModelIndex& index, int role) const {
  QVariant ret;
  if (!index.isValid())
    return ret;

  switch (role) {
    case IconRole:
      ret = this->applist[index.row()].iconPath();
      break;
    case NameRole:
      ret = this->applist[index.row()].name();
      break;
    case IdRole:
      ret = this->applist[index.row()].id();
      break;
    case VersionRole:
      ret = this->applist[index.row()].version();
      break;
    case DescriptionRole:
      ret = this->applist[index.row()].description();
      break;
    case AuthorRole:
      ret = this->applist[index.row()].author();
      break;
    case ServerIdRole:
      ret = this->applist[index.row()].serverId();
      break;
    case CategoryNameRole:
      ret = this->applist[index.row()].categoryName();
      break;
    case CreatedTimeRole:
      ret = this->applist[index.row()].createdTime();
      break;
    case StateRole:
      ret = this->applist[index.row()].state();
      break;
    case StateTextRole:
      switch (this->applist[index.row()].state()) {
        case AppInfo::Install:
          ret = "Install";
          break;
        case AppInfo::Update:
          ret = "Update";
          break;
        case AppInfo::Launch:
          ret = "Launch";
          break;
        case AppInfo::Downloading:
          ret = "Downloading";
          break;
        case AppInfo::Installing:
          ret = "Installing";
          break;
        default:
          break;
      }
      break;
    case ProgressRole:
      ret = this->applist[index.row()].progress();
      break;
    default:
      break;
  }

  return ret;
}

bool ServerAppModel::setData(const QModelIndex& index,
                             const QVariant& value,
                             int role) {
  if (!index.isValid())
    return false;

  switch (role) {
    case StateRole:
      applist[index.row()].setState(value.toInt());
      break;
    case ProgressRole:
      applist[index.row()].setProgress(value.toReal());
      break;
    default:
      return false;
      break;
  }

  emit dataChanged(index, index);
  return true;
}

bool ServerAppModel::removeRows(int row, int count, const QModelIndex&) {
  QList<AppInfo>::iterator begin = applist.begin() + row;
  QList<AppInfo>::iterator end = begin + count;

  applist.erase(begin, end);

  return true;
}

QHash<int, QByteArray> ServerAppModel::roleNames() const {
  QHash<int, QByteArray> roles;
  roles[IconRole] = "icon";
  roles[NameRole] = "name";
  roles[IdRole] = "id";
  roles[VersionRole] = "version";
  roles[DescriptionRole] = "description";
  roles[AuthorRole] = "author";
  roles[ServerIdRole] = "appid";
  roles[CategoryNameRole] = "category";
  roles[CreatedTimeRole] = "createdtime";
  roles[StateRole] = "state";
  roles[StateTextRole] = "statetext";
  roles[ProgressRole] = "progress";
  return roles;
}

QString ServerAppModel::id(int i) const {
  return data(index(i), IdRole).toString();
}

QString ServerAppModel::name(int i) const {
  return data(index(i), NameRole).toString();
}

QString ServerAppModel::stateText(int i) const {
  return data(index(i), StateTextRole).toString();
}

int ServerAppModel::launch(const QString& application) {
  int result = -1;
  HMI_DEBUG("launch", "ApplicationLauncher launch %s.",
            application.toStdString().c_str());

  result = afm_user_daemon_proxy->start(application).value().toInt();
  HMI_DEBUG("launch", "ApplicationLauncher pid: %d.", result);

  return result;
}

void ServerAppModel::appChanged(const QString& info) {
  QJsonDocument japps = QJsonDocument::fromJson(info.toUtf8());
  QJsonObject const& jso = japps.object();
  QString operation = jso["operation"].toString();
  QString id = jso["data"].toString();
  if (operation == "uninstall") {
    for (int i = 0; i < applist.size(); ++i) {
      if (applist.at(i).id() == id) {
        setData(index(i), AppInfo::Install, StateRole);
        break;
      }
    }
  }
}

void ServerAppModel::install(int i) {
  setData(index(i), AppInfo::Downloading, StateRole);

  QString url = getWgtUrl(applist[i].wgtPath());
  HttpClient client(url);

  client.download(
      getDownloadFilePath(applist[i].fileName()),
      [this, i](const QString&) {
        setData(index(i), AppInfo::Installing, StateRole);

        QTimer::singleShot(3000, this, [this, i] {
          QString installResult = afm_user_daemon_proxy->install(
              getDownloadFilePath(applist[i].fileName()));

          setData(index(i),
                  installResult.isEmpty() ? AppInfo::Install : AppInfo::Launch,
                  StateRole);
          setData(index(i), 0.0, ProgressRole);
        });
      },
      [this, i](const QString& error) {
        HMI_ERROR("ERROR", "%s", error.toStdString().c_str());

        setData(index(i), AppInfo::Install, StateRole);
        setData(index(i), 0.0, ProgressRole);
      },
      [this, i](const qint64 bytesReceived, const qint64 bytesTotal) {
        qreal progressValue = ((qreal)bytesReceived / bytesTotal) * 100;
        setData(index(i), progressValue, ProgressRole);
      });
}

void ServerAppModel::clearList() {
  beginResetModel();
  applist.clear();
  endResetModel();
}

void ServerAppModel::getPrevPage(int PageIndex, QString name, int type) {
  this->getAppPage(PageIndex > 0 ? PageIndex - 1 : 0, PAGE_SIZE, name, type,
                   true);
}

void ServerAppModel::getNextPage(int PageIndex, QString name, int type) {
  this->getAppPage(applist.count() >= PAGE_SIZE ? PageIndex + 1 : PageIndex,
                   PAGE_SIZE, name, type);
}

void ServerAppModel::setNativeApplist(const QList<AppInfo>& applist) {
  nativeApplist.clear();
  nativeApplist = applist;
  checkAppState();
}

void ServerAppModel::checkAppState() {
  if (applist.isEmpty() || nativeApplist.isEmpty()) {
    return;
  }

  QList<AppInfo>::iterator it;
  int idx = 0;
  for (it = applist.begin(); it != applist.end(); ++it) {
    QList<AppInfo>::iterator nit;
    for (nit = nativeApplist.begin(); nit != nativeApplist.end(); ++nit) {
      if ((*it).id() == (*nit).id()) {
        setData(index(idx),
                (*it).version() != (*nit).version() ? AppInfo::Update
                                                    : AppInfo::Launch,
                StateRole);
        break;
      }
    }
    idx++;
  }
}

void ServerAppModel::getAppPage(int pageIndex,
                                int pageSize,
                                QString name,
                                int type,
                                bool isPrev) {
  QString url =
      getUrlWithPage(SERVER_API_LIST, pageIndex * pageSize, pageSize)
          .append(QString("&appDeviceTypeId=%1").arg(QString::number(0)));
  if (!name.isEmpty()) {
    // get search list by name
    url.append(QString("&keyWord=%1").arg(name));
  }
  if (type != -1) {
    // get search list by type
    url.append(QString("&appTypeId=%1").arg(QString::number(type)));
  }
  HMI_ERROR("Message", "%s", url.toStdString().c_str());

  HttpClient client(url);

  client.get(
      [=](const QString& result) {
        QJsonDocument jresult = QJsonDocument::fromJson(result.toUtf8());
        QJsonObject const& jro = jresult.object();
        QJsonArray const& jappList = jro["pagination_data"].toArray();

        int offsetSize = 0;
        bool distinct = true;

        beginResetModel();
        if (isPrev || applist.size() < PAGE_SIZE || jappList.size() == PAGE_SIZE) {
          applist.clear();
          distinct = false;
        } else {
          //offsetSize = 0;
          if (applist.size() > PAGE_SIZE) {
            for (int i = 0; i < (applist.size() - PAGE_SIZE); i++) {
              applist.removeLast();
              offsetSize--;
            }
          }
        }

        for (auto const& app : jappList) {
          QJsonObject const& jso = app.toObject();

          AppInfo appinfo;
          appinfo.readFromServer(jso);
          if (distinct) {
            if (applist.contains(appinfo)) {
              continue;
            } else {
              offsetSize++;
            }
          }
          applist.append(appinfo);
        }
        endResetModel();
        checkAppState();

        emit requestCompleted(offsetSize, pageIndex, PAGE_SIZE, isPrev);
      },
      [=](const QString& msg) {
        HMI_ERROR("response", "response error: %s", msg.toStdString().c_str());
      });
}