/*
 * This file is part of the planetblupi source code
 * Copyright (C) 1997, Daniel Roux & EPSITEC SA
 * Copyright (C) 2017, Mathieu Schroeter
 * http://epsitec.ch; http://www.blupi.org; http://github.com/blupi-games
 *
 * This program 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.
 *
 * This program 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 this program. If not, see http://gnu.org/licenses
 */

#include <stdio.h>
#include <stdlib.h>

#include "button.h"
#include "decor.h"
#include "def.h"
#include "event.h"
#include "gettext.h"
#include "misc.h"
#include "pixmap.h"
#include "sound.h"

CButton::CButton ()
{
  m_type       = 0;
  m_bEnable    = true;
  m_bHide      = false;
  m_state      = 0;
  m_mouseState = 0;
  m_nbMenu     = 0;
  m_nbToolTips = 0;
  m_selMenu    = 0;
  m_bMouseDown = false;
  m_message    = static_cast<Uint32> (-1);
  m_pPixmap    = nullptr;
  m_pDecor     = nullptr;
  m_pSound     = nullptr;
  m_toolTips   = nullptr;
}

CButton::~CButton ()
{
}

// Crée un nouveau bouton.

bool
CButton::Create (
  CPixmap * pPixmap, CSound * pSound, Point pos, Sint32 type, Sint32 * pMenu,
  Sint32 nbMenu, const char ** pToolTips, Sint32 region, Uint32 message)
{
  Point  iconDim;
  Sint32 i, icon;

  static Sint32 ttypes[] = {
    DIMBUTTONX, DIMBUTTONY, // button00.bmp
  };

  if (type < 0 || type > 0)
    return false;

  iconDim.x = ttypes[type * 2 + 0];
  iconDim.y = ttypes[type * 2 + 1];

  m_pPixmap    = pPixmap;
  m_pSound     = pSound;
  m_type       = type;
  m_bEnable    = true;
  m_bHide      = false;
  m_message    = message;
  m_pos        = pos;
  m_dim        = iconDim;
  m_nbMenu     = nbMenu;
  m_selMenu    = 0;
  m_state      = 0;
  m_mouseState = 0;
  m_bMouseDown = false;

  m_nbToolTips = 0;
  while (pToolTips[m_nbToolTips])
    ++m_nbToolTips;

  for (i = 0; i < nbMenu; i++)
  {
    icon = pMenu[i];

    if (region == 1) // palmiers ?
    {
      if (icon == 0)
        icon = 90; // sol normal
      if (icon == 1)
        icon = 91; // sol inflammable
      if (icon == 2)
        icon = 92; // sol inculte
      if (icon == 7)
        icon = 9; // plante
      if (icon == 8)
        icon = 10; // arbre
    }

    if (region == 2) // hiver ?
    {
      if (icon == 0)
        icon = 96; // sol normal
      if (icon == 1)
        icon = 97; // sol inflammable
      if (icon == 2)
        icon = 98; // sol inculte
      if (icon == 8)
        icon = 99; // arbre
    }

    if (region == 3) // sapin ?
    {
      if (icon == 0)
        icon = 102; // sol normal
      if (icon == 1)
        icon = 103; // sol inflammable
      if (icon == 2)
        icon = 104; // sol inculte
      if (icon == 8)
        icon = 105; // arbre
    }

    m_iconMenu[i] = icon;
  }

  m_toolTips = pToolTips;

  return true;
}

// Dessine un bouton dans son état.

void
CButton::Draw ()
{
  Sint32 i;
  Point  pos;
  Rect   rect;

  if (m_bHide) // bouton caché ?
  {
    rect.left   = m_pos.x;
    rect.right  = m_pos.x + m_dim.x;
    rect.top    = m_pos.y;
    rect.bottom = m_pos.y + m_dim.y;
    m_pPixmap->DrawPart (-1, CHBACK, m_pos, rect); // dessine le fond
    return;
  }

  if (m_bEnable) // bouton actif ?
    m_pPixmap->DrawIcon (-1, CHBUTTON + m_type, m_mouseState, m_pos);
  else
    m_pPixmap->DrawIcon (-1, CHBUTTON + m_type, 4, m_pos);

  if (m_nbMenu == 0)
    return;

  pos = m_pos;
  if (m_nbMenu > 0)
  {
    m_pPixmap->DrawIcon (-1, CHBUTTON + m_type, m_iconMenu[m_selMenu] + 6, pos);
  }

  if (m_nbMenu == 1 || !m_bEnable || !m_bMouseDown)
    return;

  pos = m_pos;
  pos.x += m_dim.x + 2;
  for (i = 0; i < m_nbMenu; i++)
  {
    m_pPixmap->DrawIcon (-1, CHBUTTON + m_type, i == m_selMenu ? 1 : 0, pos);
    m_pPixmap->DrawIcon (-1, CHBUTTON + m_type, m_iconMenu[i] + 6, pos);
    pos.x += m_dim.x - 1;
  }
}

Sint32
CButton::GetState ()
{
  return m_state;
}

void
CButton::SetState (Sint32 state)
{
  m_state      = state;
  m_mouseState = state;
}

Sint32
CButton::GetMenu ()
{
  return m_selMenu;
}

void
CButton::SetMenu (Sint32 menu)
{
  m_selMenu = menu;
}

bool
CButton::GetEnable ()
{
  return m_bEnable;
}

void
CButton::SetEnable (bool bEnable)
{
  m_bEnable = bEnable;
}

bool
CButton::GetHide ()
{
  return m_bHide;
}

void
CButton::SetHide (bool bHide)
{
  m_bHide = bHide;
}

// Traitement d'un événement.

bool
CButton::TreatEvent (const SDL_Event & event)
{
  Point pos;

  if (m_bHide || !m_bEnable)
    return false;

  // pos = ConvLongToPos(lParam);

  switch (event.type)
  {
  case SDL_MOUSEBUTTONDOWN:
    if (
      event.button.button != SDL_BUTTON_LEFT &&
      event.button.button != SDL_BUTTON_RIGHT)
      break;

    pos.x = event.button.x;
    pos.y = event.button.y;
    if (MouseDown (pos))
      return true;
    break;

  case SDL_MOUSEMOTION:
    pos.x = event.motion.x;
    pos.y = event.motion.y;
    if (MouseMove (pos))
      return true;
    break;

  case SDL_MOUSEBUTTONUP:
    if (
      event.button.button != SDL_BUTTON_LEFT &&
      event.button.button != SDL_BUTTON_RIGHT)
      break;

    pos.x = event.button.x;
    pos.y = event.button.y;
    if (MouseUp (pos))
      return false; // (*)
    break;
  }

  return false;
}

// (*) Tous les boutons doivent recevoir l'événement BUTTONUP !

// Indique si la souris est sur ce bouton.

bool
CButton::MouseOnButton (Point pos)
{
  return Detect (pos);
}

// Retourne le tooltips pour un bouton, en fonction
// de la position de la souris.

const char *
CButton::GetToolTips (Point pos)
{
  Sint32 width = m_dim.x;
  Sint32 rank;

  if (m_bHide || !m_bEnable)
    return nullptr;

  if (m_nbMenu > 1 && m_bMouseDown) // sous-menu déroulé ?
    width += 2 + (m_dim.x - 1) * m_nbMenu;

  if (
    pos.x < m_pos.x || pos.x > m_pos.x + width || pos.y < m_pos.y ||
    pos.y > m_pos.y + m_dim.y)
    return nullptr;

  rank = (pos.x - (m_pos.x + 2 + 1)) / (m_dim.x - 1);
  if (rank < 0)
    rank = 0;
  if (rank > m_nbToolTips)
    return nullptr;

  if (m_nbMenu > 1)
  {
    if (m_bMouseDown && rank > 0)
      rank--;
    else
      rank = m_selMenu;
  }

  return gettext (m_toolTips[rank]);
}

// Détecte si la souris est dans le bouton.

bool
CButton::Detect (Point pos)
{
  Sint32 width = m_dim.x;

  if (m_bHide || !m_bEnable)
    return false;

  if (m_nbMenu > 1 && m_bMouseDown) // sous-menu déroulé ?
    width += 2 + (m_dim.x - 1) * m_nbMenu;

  if (
    pos.x < m_pos.x || pos.x > m_pos.x + width || pos.y < m_pos.y ||
    pos.y > m_pos.y + m_dim.y)
    return false;

  return true;
}

// Bouton de la souris pressé.

bool
CButton::MouseDown (Point pos)
{
  if (!Detect (pos))
    return false;

  m_mouseState = 1;
  m_bMouseDown = true;

  CEvent::PushUserEvent (EV_UPDATE);

  m_pSound->PlayImage (SOUND_CLICK, pos);
  return true;
}

// Souris déplacés.

bool
CButton::MouseMove (Point pos)
{
  bool   bDetect;
  Sint32 iState, iMenu;

  iState = m_mouseState;
  iMenu  = m_selMenu;

  bDetect = Detect (pos);

  if (m_bMouseDown)
  {
    if (bDetect)
      m_mouseState = 1; // pressé
    else
      m_mouseState = m_state;
  }
  else
  {
    if (bDetect)
      m_mouseState = m_state + 2; // survollé
    else
      m_mouseState = m_state;
  }

  if (
    m_nbMenu > 1 && m_bMouseDown &&
    pos.x > m_pos.x + m_dim.x + 2) // dans sous-menu ?
  {
    m_selMenu = (pos.x - (m_pos.x + m_dim.x + 2)) / (m_dim.x - 1);
    if (m_selMenu >= m_nbMenu)
      m_selMenu = m_nbMenu - 1;
  }

  if (iState != m_mouseState || iMenu != m_selMenu)
    CEvent::PushUserEvent (EV_UPDATE);

  return m_bMouseDown;
}

// Bouton de la souris relâché.

bool
CButton::MouseUp (Point pos)
{
  bool bDetect;

  bDetect = Detect (pos);

  m_mouseState = m_state;
  m_bMouseDown = false;

  if (!bDetect)
    return false;

  if (m_message != static_cast<Uint32> (-1))
    CEvent::PushUserEvent (m_message);

  return true;
}