/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include "prcntfld.hxx"

PercentField::PercentField()
    : m_pField(nullptr)
    , nRefValue(0)
    , nOldMax(0)
    , nOldMin(0)
    , nOldSpinSize(0)
    , nOldBaseValue(0)
    , nLastPercent(-1)
    , nLastValue(-1)
    , nOldDigits(0)
    , eOldUnit(FUNIT_NONE)
    , bLockAutoCalculation(false)
{
}

void PercentField::set(MetricField *pField)
{
    m_pField = pField;
    nOldSpinSize = m_pField->GetSpinSize();
    nRefValue = DenormalizePercent(m_pField->GetMax(FUNIT_TWIP));
    nOldDigits = m_pField->GetDecimalDigits();
    m_pField->SetCustomUnitText(OUString('%'));
}

void PercentField::SetRefValue(sal_Int64 nValue)
{
    sal_Int64 nRealValue = GetRealValue(eOldUnit);

    nRefValue = nValue;

    if (!bLockAutoCalculation && (m_pField->GetUnit() == FUNIT_CUSTOM))
        SetPrcntValue(nRealValue, eOldUnit);
}

void PercentField::ShowPercent(bool bPercent)
{
    if ((bPercent && m_pField->GetUnit() == FUNIT_CUSTOM) ||
        (!bPercent && m_pField->GetUnit() != FUNIT_CUSTOM))
        return;

    sal_Int64 nOldValue;

    if (bPercent)
    {
        sal_Int64 nAktWidth, nPercent;

        nOldValue = GetValue();

        eOldUnit = m_pField->GetUnit();
        nOldDigits = m_pField->GetDecimalDigits();
        nOldMin = m_pField->GetMin();
        nOldMax = m_pField->GetMax();
        nOldSpinSize = m_pField->GetSpinSize();
        nOldBaseValue = m_pField->GetBaseValue();
        m_pField->SetUnit(FUNIT_CUSTOM);
        m_pField->SetDecimalDigits( 0 );

        nAktWidth = MetricField::ConvertValue(nOldMin, 0, nOldDigits, eOldUnit, FUNIT_TWIP);
        // round to 0.5 percent
        nPercent = ((nAktWidth * 10) / nRefValue + 5) / 10;

        m_pField->SetMin(std::max(static_cast< sal_Int64 >(1), nPercent));
        m_pField->SetMax(100);
        m_pField->SetSpinSize(5);
        m_pField->SetBaseValue(0);
        if (nOldValue != nLastValue)
        {
            nAktWidth = MetricField::ConvertValue(nOldValue, 0, nOldDigits, eOldUnit, FUNIT_TWIP);
            nPercent = ((nAktWidth * 10) / nRefValue + 5) / 10;
            m_pField->SetValue(nPercent);
            nLastPercent = nPercent;
            nLastValue = nOldValue;
        }
        else
            m_pField->SetValue(nLastPercent);
    }
    else
    {
        sal_Int64 nOldPercent = GetValue(FUNIT_CUSTOM);

        nOldValue = Convert(GetValue(), m_pField->GetUnit(), eOldUnit);

        m_pField->SetUnit(eOldUnit);
        m_pField->SetDecimalDigits(nOldDigits);
        m_pField->SetMin(nOldMin);
        m_pField->SetMax(nOldMax);
        m_pField->SetSpinSize(nOldSpinSize);
        m_pField->SetBaseValue(nOldBaseValue);

        if (nOldPercent != nLastPercent)
        {
            SetPrcntValue(nOldValue, eOldUnit);
            nLastPercent = nOldPercent;
            nLastValue = nOldValue;
        }
        else
            SetPrcntValue(nLastValue, eOldUnit);
    }
}

void PercentField::SetPrcntValue(sal_Int64 nNewValue, FieldUnit eInUnit)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM || eInUnit == FUNIT_CUSTOM)
        m_pField->SetValue(Convert(nNewValue, eInUnit, m_pField->GetUnit()));
    else
    {
        // Overwrite output value, do not restore later
        sal_Int64 nPercent, nAktWidth;
        if(eInUnit == FUNIT_TWIP)
        {
            nAktWidth = MetricField::ConvertValue(nNewValue, 0, nOldDigits, FUNIT_TWIP, FUNIT_TWIP);
        }
        else
        {
            sal_Int64 nValue = Convert(nNewValue, eInUnit, eOldUnit);
            nAktWidth = MetricField::ConvertValue(nValue, 0, nOldDigits, eOldUnit, FUNIT_TWIP);
        }
        nPercent = ((nAktWidth * 10) / nRefValue + 5) / 10;
        m_pField->SetValue(nPercent);
    }
}

void PercentField::SetBaseValue(sal_Int64 nNewValue, FieldUnit eInUnit)
{
    if (m_pField->GetUnit() == FUNIT_CUSTOM)
        nOldBaseValue = MetricField::ConvertValue(nNewValue, 0, nOldDigits, eInUnit, eOldUnit);
    else
        m_pField->SetBaseValue(nNewValue, eInUnit);
}

sal_Int64 PercentField::GetValue(FieldUnit eOutUnit)
{
    return Convert(m_pField->GetValue(), m_pField->GetUnit(), eOutUnit);
}

void PercentField::SetMin(sal_Int64 nNewMin, FieldUnit eInUnit)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM)
        m_pField->SetMin(nNewMin, eInUnit);
    else
    {
        if (eInUnit == FUNIT_NONE)
            eInUnit = eOldUnit;
        nOldMin = Convert(nNewMin, eInUnit, eOldUnit);

        sal_Int64 nPercent = Convert(nNewMin, eInUnit, FUNIT_CUSTOM);
        m_pField->SetMin(std::max( static_cast< sal_Int64 >(1), nPercent));
    }
}

void PercentField::SetMax(sal_Int64 nNewMax, FieldUnit eInUnit)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM)
        m_pField->SetMax(nNewMax, eInUnit);
}

sal_Int64 PercentField::NormalizePercent(sal_Int64 nValue)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM)
        nValue = m_pField->Normalize(nValue);
    else
        nValue = nValue * ImpPower10(nOldDigits);
    return nValue;
}

sal_Int64 PercentField::DenormalizePercent(sal_Int64 nValue)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM)
        nValue = m_pField->Denormalize(nValue);
    else
    {
        sal_Int64 nFactor = ImpPower10(nOldDigits);
        nValue = ((nValue+(nFactor/2)) / nFactor);
    }
    return nValue;
}

bool PercentField::IsValueModified()
{
    if (m_pField->GetUnit() == FUNIT_CUSTOM)
        return true;
    else
        return m_pField->IsValueModified();
}

void PercentField::SetUserValue(sal_Int64 nNewValue, FieldUnit eInUnit)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM || eInUnit == FUNIT_CUSTOM)
        m_pField->SetUserValue(Convert(nNewValue, eInUnit, m_pField->GetUnit()),FUNIT_NONE);
    else
    {
        // Overwrite output value, do not restore later
        sal_Int64 nPercent, nAktWidth;
        if (eInUnit == FUNIT_TWIP)
        {
            nAktWidth = MetricField::ConvertValue(nNewValue, 0, nOldDigits, FUNIT_TWIP, FUNIT_TWIP);
        }
        else
        {
            sal_Int64 nValue = Convert(nNewValue, eInUnit, eOldUnit);
            nAktWidth = MetricField::ConvertValue(nValue, 0, nOldDigits, eOldUnit, FUNIT_TWIP);
        }
        nPercent = ((nAktWidth * 10) / nRefValue + 5) / 10;
        m_pField->SetUserValue(nPercent,FUNIT_NONE);
    }
}

sal_Int64 PercentField::ImpPower10(sal_uInt16 n)
{
    sal_Int64 nValue = 1;

    for (sal_uInt16 i=0; i < n; ++i)
        nValue *= 10;

    return nValue;
}

sal_Int64 PercentField::GetRealValue(FieldUnit eOutUnit)
{
    if (m_pField->GetUnit() != FUNIT_CUSTOM)
        return GetValue(eOutUnit);
    else
        return Convert(GetValue(), m_pField->GetUnit(), eOutUnit);
}

sal_Int64 PercentField::Convert(sal_Int64 nValue, FieldUnit eInUnit, FieldUnit eOutUnit)
{
    if (eInUnit == eOutUnit ||
        (eInUnit == FUNIT_NONE && eOutUnit == m_pField->GetUnit()) ||
        (eOutUnit == FUNIT_NONE && eInUnit == m_pField->GetUnit()))
        return nValue;

    if (eInUnit == FUNIT_CUSTOM)
    {
        // Convert to metric
        sal_Int64 nTwipValue = (nRefValue * nValue + 50) / 100;

        if (eOutUnit == FUNIT_TWIP) // Only convert if necessary
            return NormalizePercent(nTwipValue);
        else
            return MetricField::ConvertValue(NormalizePercent(nTwipValue), 0, nOldDigits, FUNIT_TWIP, eOutUnit);
    }

    if (eOutUnit == FUNIT_CUSTOM)
    {
        // Convert to percent
        sal_Int64 nAktWidth;
        nValue = DenormalizePercent(nValue);

        if (eInUnit == FUNIT_TWIP)  // Only convert if necessary
            nAktWidth = nValue;
        else
            nAktWidth = MetricField::ConvertValue(nValue, 0, nOldDigits, eInUnit, FUNIT_TWIP);
        // Round to 0.5 percent
        return ((nAktWidth * 1000) / nRefValue + 5) / 10;
    }

    return MetricField::ConvertValue(nValue, 0, nOldDigits, eInUnit, eOutUnit);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
