/*Copyright (C) 2010-2020 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "FightCameraHandler.hpp"

FightCameraHandler::FightCameraHandler(const Box &arenaBox, float groundLevel, const std::vector<const Player*> &players, unsigned int mode) :
m_players(players), m_arenaBox(arenaBox), m_groundLevel(groundLevel), m_mode(mode)
{
    m_arenaRatio = (arenaBox.height > 0) ? arenaBox.width / arenaBox.height : 1.f;
}

void FightCameraHandler::look(float viewWidth, float viewHeight) const
{
    /// Skip the computation if the view is too large for the arena.
    if (m_mode == MODE_DYNAMIC &&
        m_arenaBox.width >= viewWidth && m_arenaBox.height >= viewHeight)
    {
        Box view(0, 0, viewWidth, viewHeight);
        computeView(view, viewWidth);
        
        Camera::look(view, viewWidth, viewHeight);
    }
    else
    {
        Camera::look(m_arenaBox, viewWidth, viewHeight);
    }
}


void FightCameraHandler::computeView(Box &view, float minWidth) const
{
    emcompass(view);
    extend(view, minWidth);
    adjust(view);
    center(view);
}

///Returns the smallest encompassing box containing every boxes
///specified in \p innerBoxes.
void FightCameraHandler::emcompass(Box &box) const
{
    if (!m_players.empty())
    {
        const Box &box0 = m_players[0]->getBox();
        
        float left = box0.left, top = box0.top,
            right = box0.left + box0.width, bottom = box0.top + box0.height;
        
        for (unsigned int i = 1; i < m_players.size(); i++)
        {
            const Box &p_box = m_players[i]->getBox();
            
            if (p_box.left < left) left = p_box.left;
            if (p_box.top < top) top = p_box.top;
            if (p_box.left + p_box.width > right) right = p_box.left + p_box.width;
            if (p_box.top + p_box.height > bottom) bottom = p_box.top + p_box.height;
        }
        
        box = Box(left, top, right - left, bottom - top);
    }
}

void FightCameraHandler::extend(Box &box, float minWidth) const
{
    //Always show the ground.
    box.height += (m_arenaBox.top + m_arenaBox.height) - (box.top + box.height);
    
    //Add some padding
    float width_left = m_arenaBox.width - box.width,
        height_left = m_arenaBox.height - box.height;
        
    float side_padding = std::min(width_left / 2 - 1, PADDING);
    float top_padding = std::min(height_left, PADDING);
    
    box.left -= side_padding;
    box.width += side_padding * 2;
    box.top -= top_padding;
    box.height += top_padding;
    
    //Respect the minimum width.
    if (box.width < minWidth)
    {
        box.left -= (minWidth - box.width) / 2;
        box.width = minWidth;
    }
}

void FightCameraHandler::adjust(Box &box) const
{
    float ratio = box.width / box.height;
    
    if (ratio > m_arenaRatio) //Height should be adjusted
    {
        float adjustedHeight = box.width / m_arenaRatio;
        box.height = adjustedHeight;
    }
    else if (ratio < m_arenaRatio) //Width should be adjusted
    {
        float adjustedWidth = m_arenaRatio * box.height;
        box.width = adjustedWidth;
    }
}

///Avoids the box \p box to be outside the arena box as much as possible.
///(As long as the ratio of the arena and of the box are alike, this should
///never happen).
void FightCameraHandler::center(Box &box) const
{
    /// Keeps the ground of the arena at the same place relative to the area.
    if (m_arenaBox.top + m_arenaBox.height > 0)
    {
        float ratioGround = (m_arenaBox.top + m_groundLevel) / (m_arenaBox.top + m_arenaBox.height);
        box.top = (m_arenaBox.top + m_groundLevel) - ratioGround * box.height;
    }
    
    /// Fixes box out of the arena box.
    if (box.left < m_arenaBox.left)
        box.left = m_arenaBox.left;
    
    if (box.top < m_arenaBox.top)
        box.top = m_arenaBox.top;
        
    if (box.left + box.width > m_arenaBox.left + m_arenaBox.width)
        box.left -= (box.left + box.width) - (m_arenaBox.left + m_arenaBox.width);
    
    if (box.top + box.height > m_arenaBox.top + m_arenaBox.height)
        box.top -= (box.top + box.height) - (m_arenaBox.top + m_arenaBox.height);
}
