/*
 * Copyright (c) 2021, Technische Universität Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors:
 *    Iron Prando da Silva
 */

#include "phasedependency.h"
#include "phase.h"
#include <iostream>

PhaseDependency::PhaseDependency(DependencyType type, QString timeDependency, std::shared_ptr<Phase> dependency)
{
    mType = type;
    mTimeDependency = timeDependency;
    mDependency = dependency;
}

PhaseDependency::PhaseDependency(DependencyType type, QString timeDependency)
{
    mType = type;
    mTimeDependency = timeDependency;
    mDependency = nullptr;
    mIsInvisible = true;
}

PhaseDependency::~PhaseDependency()
{
}

bool PhaseDependency::draw(QPoint &end, const TraceDrawingProperties &drawingProperties, QPainter *painter,
                           const QwtScaleMap &xMap, const QwtScaleMap &yMap)
{
    if (mIsInvisible)
        return false;

    traceTime depBegin = mDependency->span.Begin();
    traceTime depEnd = mDependency->span.End();

    if (xMap.transform(depEnd) < 0)
        return false;

    bool drawn = false;
    for (auto yVal : mDependency->getYVals(drawingProperties))
    {
        mDraw(end, yVal, drawingProperties, painter, xMap, yMap);
        drawn = true;
    }

    return drawn;
}

void PhaseDependency::mDraw(QPoint &end, double depY, const TraceDrawingProperties &drawingProperties,
                            QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap)
{
    traceTime depBegin = mDependency->span.Begin();
    traceTime depEnd = mDependency->span.End();

    traceTime depOffset = (depBegin == depEnd) ? static_cast<traceTime>(0.05 * mDependency->clk) : 0;

    double depYVal = yMap.transform(depY);
    double depSymbolHeight = yMap.transform(0) - yMap.transform(mDependency->hexagonHeight);
    QPoint depLineFrom(static_cast<int>(xMap.transform(depBegin /* + (depEnd + depOffset - depBegin)/4*/)),
                       static_cast<int>(depYVal));

    QLineF line(depLineFrom, end);
    double angle = std::atan2(-line.dy(), line.dx());

    qreal arrowSize = 10;
    QPointF arrowP1 = line.p2() - QPointF(sin(angle + M_PI / 3) * arrowSize, cos(angle + M_PI / 3) * arrowSize);
    QPointF arrowP2 =
        line.p2() - QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize, cos(angle + M_PI - M_PI / 3) * arrowSize);
    QPolygonF arrowHead;
    arrowHead << line.p2() << arrowP1 << arrowP2;

    QColor color = mDependency->getColor(drawingProperties);
    painter->setBrush(QBrush(color, mDependency->getBrushStyle()));

    painter->drawLine(line);
    painter->drawPolygon(arrowHead);

    if (drawingProperties.drawDependenciesOption.text == DependencyTextOption::Enabled)
    {
        QPoint textPosition(line.x1() + (line.x2() - line.x1()) / 2, line.y1() + (line.y2() - line.y1()) / 2);
        auto alignment = TextPositioning::topRight;

        if (textPosition.y() == line.y1())
        {
            alignment = TextPositioning::topCenter;
        }
        else if (textPosition.x() == line.x1())
        {
            if (line.y1() > line.y2())
            {
                alignment = TextPositioning::bottomRight;
            }
        }

        drawText(painter, mTimeDependency, textPosition, alignment);
    }
}

QString PhaseDependency::dependencyTypeName(DependencyType dtype) {
    switch(dtype) {
        case IntraBank:
            return "IntraBank";

        case IntraBankGroup:
            return "IntraBankGroup";
        
        case IntraBankInGroup:
            return "IntraBankInGroup";

        case IntraRank:
            return "IntraRank";

        case IntraLogicalRank:
            return "IntraLogicalRank";

        case IntraPhysicalRank:
            return "IntraPhysicalRank";

        case IntraDIMMRank:
            return "IntraDIMMRank";

        case InterRank:
            return "InterRank";

        case InterDIMMRank:
            return "InterDIMMRank";

        default:
            // TODO - maybe throw?
            return "";

    }
}
