// -*- mode: c++; c-file-style: "linux"; c-basic-offset: 2; indent-tabs-mode:
// nil -*-
//
//  Copyright (C) 2004-2015 Andrej Vodopivec <andrej.vodopivec@gmail.com>
//            (C) 2014-2018 Gunter Königsmann <wxMaxima@physikbuch.de>
//
//  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 2 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
//  MERCHGroupCell *groupANTABILITY 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, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//  SPDX-License-Identifier: GPL-2.0+

/*! \file
  This file defines the class DiffCell

  DiffCell is the Cell type that represents the field that represents the diff()
  command.
*/

#include "DiffCell.h"
#include "CellImpl.h"
#include "GroupCell.h"
#include "TextCell.h"
#include <wx/config.h>
#include <utility>
#include <memory>

DiffCell::DiffCell(GroupCell *group, Configuration *config,
                   std::unique_ptr<Cell> &&base, std::unique_ptr<Cell> &&diff)
  : Cell(group, config), m_baseCell(std::move(base)),
    m_diffCell(std::move(diff))

{
  InitBitFields_DiffCell();
  SetStyle(TS_VARIABLE);
  m_diffCell->SetSuppressMultiplicationDot(true);
  if(m_baseCell)
    {
      if((m_baseCell->ToString() == wxS("*")) ||
         (m_baseCell->ToString() == wxS("\u00B7")))
      m_baseCell->Hide(true);
    }
  MakeBreakupCells();
}

DiffCell::DiffCell(GroupCell *group, const DiffCell &cell)
  : DiffCell(group, cell.m_configuration,
             CopyList(group, cell.m_baseCell.get()),
             CopyList(group, cell.m_diffCell.get())) {
  CopyCommonData(cell);
  MakeBreakupCells();
}

DEFINE_CELL(DiffCell)

void DiffCell::MakeBreakupCells() {
  if (m_open)
    return;
  m_open = std::make_unique<TextCell>(m_group, m_configuration, "diff(");
  m_comma = std::make_unique<TextCell>(m_group, m_configuration, ",");
  m_close = std::make_unique<TextCell>(m_group, m_configuration, ")");
}

void DiffCell::Recalculate(AFontSize fontsize) const {
  if (NeedsRecalculation(fontsize)) {
    m_baseCell->RecalculateList(fontsize);
    m_diffCell->RecalculateList(fontsize);
    if (!IsBrokenIntoLines()) {
      m_width = m_baseCell->SumOfWidths() + m_diffCell->SumOfWidths();
      m_center = std::max(m_diffCell->GetCenterList(), m_baseCell->GetCenterList());
      m_height =
        m_center + std::max(m_diffCell->GetMaxDrop(), m_baseCell->GetMaxDrop());
    } else {
      m_width = m_center = m_height = 0;
      m_open->RecalculateList(fontsize);
      m_comma->RecalculateList(fontsize);
      m_close->RecalculateList(fontsize);
    }
    Cell::Recalculate(fontsize);
  }
}

void DiffCell::Draw(wxPoint point, wxDC *dc, wxDC *antialiassingDC) {
  Cell::Draw(point, dc, antialiassingDC);
  if (DrawThisCell(point) && InUpdateRegion()) {
    wxPoint bs, df;
    df.x = point.x;
    df.y = point.y;
    m_diffCell->DrawList(df, dc, antialiassingDC);

    bs.x = point.x + m_diffCell->SumOfWidths();
    bs.y = point.y;
    m_baseCell->DrawList(bs, dc, antialiassingDC);
  }
}

wxString DiffCell::ToString() const {
  if (IsBrokenIntoLines())
    return wxEmptyString;
  const Cell *tmp = m_baseCell->GetNext();
  wxString s = wxS("'diff(");
  if (tmp != NULL)
    s += tmp->ListToString();
  s += m_diffCell->ListToString();
  s += wxS(")");

  // If we use the 1st derivation we can do without the trailing ",1":
  if (s.EndsWith(",1)"))
    s = s.Left(s.Length()-3) + ")";
  return s;
}

wxString DiffCell::ToMatlab() const {
  if (IsBrokenIntoLines())
    return wxEmptyString;
  const Cell *tmp = m_baseCell->GetNext();
  wxString s = wxS("'diff(");
  if (tmp != NULL)
    s += tmp->ListToMatlab();
  s += m_diffCell->ListToMatlab();
  s += wxS(")");
  return s;
}

wxString DiffCell::ToTeX() const {
  if (IsBrokenIntoLines())
    return wxEmptyString;
  wxString diff = m_diffCell->ListToTeX();
  wxString function = m_baseCell->ListToTeX();

  if (m_configuration->UsePartialForDiff())
    diff.Replace(wxS("\\frac{d}{d"), wxS("\\frac{\\partial}{\\partial"));

  wxString s = diff + function;
  return s;
}

wxString DiffCell::ToMathML() const {
  wxString retval;

  retval = wxS("<mrow>") + m_diffCell->ListToMathML();
  if (m_baseCell)
    retval += m_baseCell->ListToMathML();
  retval += wxS("</mrow>\n");
  // retval = wxS("<apply><diff/><ci>") + m_diffCell->ListToMathML() +
  // wxS("</ci>"); if(m_baseCell)
  //   retval += wxS("<ci>") + m_baseCell->ListToMathML() + wxS("</ci>") ;
  // retval += wxS("</apply>");
  return retval;
}

wxString DiffCell::ToOMML() const {
  wxString retval;

  retval = m_diffCell->ListToOMML();
  if (m_baseCell)
    retval += m_baseCell->ListToOMML();

  return retval;
}

wxString DiffCell::ToXML() const {
  wxString flags;
  if (HasHardLineBreak())
    flags += wxS(" breakline=\"true\"");

  return wxS("<d") + flags + wxS(">") + m_diffCell->ListToXML() +
    m_baseCell->ListToXML() + wxS("</d>");
}

bool DiffCell::BreakUp() const {
  if (IsBrokenIntoLines())
    return false;

  Cell::BreakUpAndMark();
  m_open->SetNextToDraw(m_baseCell);
  m_baseCell->last()->SetNextToDraw(m_comma);
  m_comma->SetNextToDraw(m_diffCell);
  m_diffCell->last()->SetNextToDraw(m_close);
  m_close->SetNextToDraw(m_nextToDraw);
  m_nextToDraw = m_open;
  return true;
}

void DiffCell::SetNextToDraw(Cell *next) const {
  if (IsBrokenIntoLines())
    m_close->SetNextToDraw(next);
  else
    m_nextToDraw = next;
}
