// -*- c-basic-offset: 4 -*-

/** @file HFOVDialog.cpp
 *
 *  @brief implementation of HFOVDialog Class
 *
 *  @author Pablo d'Angelo <pablo.dangelo@web.de>
 *
 *  $Id$
 *
 *  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 software 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 software. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */


#include "hugin_config.h"
#include "panoinc_WX.h"
#include "panoinc.h"

#include "wxPlatform.h"
#include "platform.h"
#include "wxutils.h"
#include "LensTools.h"
#include "HFOVDialog.h"

HFOVDialog::HFOVDialog(wxWindow * parent, HuginBase::SrcPanoImage & srcImg)
    : m_srcImg(srcImg)
{
    m_HFOV = srcImg.getHFOV();
    m_focalLength = srcImg.getExifFocalLength();
    m_cropFactor = srcImg.getCropFactor();
    wxXmlResource::Get()->LoadDialog(this, parent, "dlg_focallength");

    m_cropText = XRCCTRL(*this, "lensdlg_cropfactor_text", wxTextCtrl);
    DEBUG_ASSERT(m_cropText);
    m_cropText->Bind(wxEVT_TEXT, &HFOVDialog::OnCropFactorChanged, this);

    m_hfovText = XRCCTRL(*this, "lensdlg_hfov_text", wxTextCtrl);
    DEBUG_ASSERT(m_hfovText);
    m_hfovText->Bind(wxEVT_TEXT, &HFOVDialog::OnHFOVChanged, this);

    m_focalLengthText = XRCCTRL(*this, "lensdlg_focallength_text", wxTextCtrl);
    DEBUG_ASSERT(m_focalLengthText);
    m_focalLengthText->Bind(wxEVT_TEXT, &HFOVDialog::OnFocalLengthChanged, this);

    m_projChoice = XRCCTRL(*this, "lensdlg_type_choice", wxChoice);
    DEBUG_ASSERT(m_projChoice);
    FillLensProjectionList(m_projChoice);
    m_projChoice->Bind(wxEVT_CHOICE, &HFOVDialog::OnTypeChanged, this);

    m_okButton = XRCCTRL(*this, "wxID_OK", wxButton);
    DEBUG_ASSERT(m_okButton);
    m_okButton->Bind(wxEVT_BUTTON, &HFOVDialog::OnOk, this);

    Bind(wxEVT_BUTTON, &HFOVDialog::OnLoadLensParameters, this, XRCID("lensdlg_load_lens_button"));
    // fill fields
    wxString fn(srcImg.getFilename().c_str(), HUGIN_CONV_FILENAME);
    wxString message;
    message.Printf(_("No or only partial information about field of view was found in image file\n%s\n\nPlease enter the horizontal field of view (HFOV) or the focal length and crop factor."), fn.c_str());
    XRCCTRL(*this, "lensdlg_message", wxStaticText)->SetLabel(message);
    SelectListValue(m_projChoice, m_srcImg.getProjection());

    if (m_cropFactor > 0 && m_focalLength > 0) {
        // everything is well known.. compute HFOV
        m_HFOV = HuginBase::SrcPanoImage::calcHFOV(m_srcImg.getProjection(), m_focalLength,
                          m_cropFactor, m_srcImg.getSize());

        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
        m_focalLengthStr = hugin_utils::doubleTowxString(m_focalLength,2);
        m_focalLengthText->SetValue(m_focalLengthStr);
        m_cropFactorStr = hugin_utils::doubleTowxString(m_cropFactor,2);
        m_cropText->SetValue(m_cropFactorStr);
    } else if (m_cropFactor > 0 && m_focalLength <= 0) {
        // focal length unknown
        m_cropFactorStr = hugin_utils::doubleTowxString(m_cropFactor,2);
        m_cropText->SetValue(m_cropFactorStr);
        m_okButton->Disable();
    } else if (m_cropFactor <= 0 && m_focalLength > 0) {
        // crop factor unknown
        m_focalLengthStr = hugin_utils::doubleTowxString(m_focalLength,2);
        m_focalLengthText->SetValue(m_focalLengthStr);
        m_okButton->Disable();
    } else {
        // everything unknown
        // assume a crop factor of one
        m_cropFactor = 1;
        m_cropFactorStr = hugin_utils::doubleTowxString(m_cropFactor,2);
        m_cropText->SetValue(m_cropFactorStr);
        m_okButton->Disable();
    }
    // set a proper size for this dialog
    this->GetSizer()->SetSizeHints(this);
}

void HFOVDialog::OnTypeChanged(wxCommandEvent & e)
{
    HuginBase::SrcPanoImage::Projection new_proj = (HuginBase::SrcPanoImage::Projection)(GetSelectedValue(m_projChoice));
    DEBUG_DEBUG("new type: " << new_proj);
    m_srcImg.setProjection(new_proj);
    if (m_cropFactor > 0 && m_focalLength > 0) {
        m_HFOV = HuginBase::SrcPanoImage::calcHFOV(m_srcImg.getProjection(), m_focalLength,
                          m_cropFactor, m_srcImg.getSize());
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
    }
}

void HFOVDialog::OnHFOVChanged(wxCommandEvent & e)
{
    wxString text = m_hfovText->GetValue();
    DEBUG_DEBUG("state: " <<  m_HFOVStr.mb_str(wxConvLocal) << ", change:" << text.mb_str(wxConvLocal));
    DEBUG_DEBUG("cmd str: " << e.GetString().mb_str(wxConvLocal));
    if (text.empty()) {
        // ignore all empty hfov changes
        return;
    }
    // ignore changes caused by ourself
    if (m_hfovText->GetValue() == m_HFOVStr) {
        DEBUG_DEBUG("ignoring programatic HFOV change");
        return;
    }

    // accept change
    m_HFOVStr = text;

    if (text.empty()) {
        m_HFOV = 0;
        m_okButton->Disable();
        return;
    }

    if (!hugin_utils::str2double(text, m_HFOV)) {
        m_okButton->Disable();
        return;
    }

    DEBUG_DEBUG(m_HFOV);

    if (m_HFOV <= 0) {
        hugin_utils::HuginMessageBox(_("The horizontal field of view must be positive."), _("Hugin"), wxOK | wxICON_INFORMATION, wxGetActiveWindow());
        m_HFOV = 50;
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
        return;
    }

    if (m_srcImg.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR && m_HFOV > 179) {
        DEBUG_DEBUG("HFOV " << m_HFOV << " too big, resetting to 179");
        m_HFOV=179;
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
    }

    if (m_cropFactor > 0) {
        // set focal length only if crop factor is known
        m_focalLength = HuginBase::SrcPanoImage::calcFocalLength(m_srcImg.getProjection(), m_HFOV, m_cropFactor, m_srcImg.getSize());
        m_focalLengthStr = hugin_utils::doubleTowxString(m_focalLength,2);
        m_focalLengthText->SetValue(m_focalLengthStr);
    }
    m_okButton->Enable();
}

void HFOVDialog::OnFocalLengthChanged(wxCommandEvent & e)
{
    wxString text = m_focalLengthText->GetValue();
    DEBUG_DEBUG(m_focalLengthStr.mb_str(wxConvLocal) << " => " << text.mb_str(wxConvLocal));
    // ignore changes caused by ourself
    if (m_focalLengthText->GetValue() == m_focalLengthStr) {
        DEBUG_DEBUG("ignore focal length change");
        return;
    }
    // accept change
    m_focalLengthStr = text;

    if (text.empty()) {
        m_focalLength = 0;
        return;
    }
    if (!hugin_utils::str2double(text, m_focalLength)) {
        return;
    }
    DEBUG_DEBUG(m_focalLength);

    // ignore leading zeros..
    if (m_focalLength == 0) {
        return;
    }
    if (m_focalLength <= 0) {
        m_focalLength=1;
        m_focalLengthStr = hugin_utils::doubleTowxString(m_focalLength,2);
        m_focalLengthText->SetValue(m_focalLengthStr);
        hugin_utils::HuginMessageBox(_("The focal length must be positive."), _("Hugin"), wxOK | wxICON_INFORMATION, wxGetActiveWindow());
    }

    if (m_cropFactor > 0) {
        // calculate HFOV.
        m_HFOV = HuginBase::SrcPanoImage::calcHFOV(m_srcImg.getProjection(), m_focalLength,
                          m_cropFactor, m_srcImg.getSize());
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
        m_okButton->Enable();
    }
}

void HFOVDialog::OnCropFactorChanged(wxCommandEvent & e)
{
    // ignore changesd cause by ourself
    wxString text = m_cropText->GetValue();
    DEBUG_DEBUG(m_cropFactorStr.mb_str(wxConvLocal) << " => " << text.mb_str(wxConvLocal));
    if (text == m_cropFactorStr) {
        DEBUG_DEBUG("ignore crop change");
        return;
    }
        // accept change
    m_cropFactorStr = text;

    if (text.empty()) {
        m_cropFactor = 0;
        return;
    }
    if (!hugin_utils::str2double(text, m_cropFactor)) {
        return;
    }

    // ignore leading zeros..
    if (m_cropFactor == 0) {
        m_cropFactorStr = text;
        return;
    }

    if (m_cropFactor <= 0) {
        hugin_utils::HuginMessageBox(_("The crop factor must be positive."), _("Hugin"), wxOK | wxICON_INFORMATION, wxGetActiveWindow());
        m_cropFactor=1;
        m_cropFactorStr = hugin_utils::doubleTowxString(m_cropFactor,2);
        m_cropText->SetValue(m_cropFactorStr);
        return;
    }

    if (m_focalLength > 0) {
        m_HFOV = HuginBase::SrcPanoImage::calcHFOV(m_srcImg.getProjection(), m_focalLength,
                          m_cropFactor, m_srcImg.getSize());
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
        m_okButton->Enable();
    }
}

void HFOVDialog::OnLoadLensParameters(wxCommandEvent & e)
{
    HuginBase::Lens lens;
    lens.setImageSize(m_srcImg.getSize());

    bool cropped=false;
    bool autoCenterCrop=false;
    vigra::Rect2D cropRect;

    if (LoadLensParametersChoose(this, lens, cropped, autoCenterCrop, cropRect)) {
        m_HFOV = lens.getHFOV();
        m_cropFactor = lens.getCropFactor();

        m_srcImg.setCropFactor(lens.getCropFactor());
        m_srcImg.setHFOV(const_map_get(lens.variables,"v").getValue());
        m_srcImg.setProjection((HuginBase::SrcPanoImage::Projection) lens.getProjection());

        m_focalLength = HuginBase::SrcPanoImage::calcFocalLength(m_srcImg.getProjection(), m_HFOV, m_cropFactor, m_srcImg.getSize());

        // geometrical distortion correction
        std::vector<double> radialDist(4);
        radialDist[0] = const_map_get(lens.variables,"a").getValue();
        radialDist[1] = const_map_get(lens.variables,"b").getValue();
        radialDist[2] = const_map_get(lens.variables,"c").getValue();
        radialDist[3] = 1 - radialDist[0] - radialDist[1] - radialDist[2];
        m_srcImg.setRadialDistortion(radialDist);
        hugin_utils::FDiff2D t;
        t.x = const_map_get(lens.variables,"d").getValue();
        t.y = const_map_get(lens.variables,"e").getValue();
        m_srcImg.setRadialDistortionCenterShift(t);
        t.x = const_map_get(lens.variables,"g").getValue();
        t.y = const_map_get(lens.variables,"t").getValue();
        m_srcImg.setShear(t);

    // vignetting
        std::vector<double> vigCorrCoeff(4);
        vigCorrCoeff[0] = const_map_get(lens.variables,"Va").getValue();
        vigCorrCoeff[1] = const_map_get(lens.variables,"Vb").getValue();
        vigCorrCoeff[2] = const_map_get(lens.variables,"Vc").getValue();
        vigCorrCoeff[3] = const_map_get(lens.variables,"Vd").getValue();
        m_srcImg.setRadialVigCorrCoeff(vigCorrCoeff);
        t.x = const_map_get(lens.variables,"Vx").getValue();
        t.y = const_map_get(lens.variables,"Vy").getValue();
        m_srcImg.setRadialVigCorrCenterShift(t);

        std::vector<float> resp(5);
        resp[0] = const_map_get(lens.variables,"Ra").getValue();
        resp[1] = const_map_get(lens.variables,"Rb").getValue();
        resp[2] = const_map_get(lens.variables,"Rc").getValue();
        resp[3] = const_map_get(lens.variables,"Rd").getValue();
        resp[4] = const_map_get(lens.variables,"Re").getValue();
        m_srcImg.setEMoRParams(resp);

        if (!cropped)
        {
            m_srcImg.setCropMode(HuginBase::SrcPanoImage::NO_CROP);
        }
        else
        {
            if (m_srcImg.isCircularCrop())
            {
                m_srcImg.setCropMode(HuginBase::SrcPanoImage::CROP_CIRCLE);
            }
            else
            {
                m_srcImg.setCropMode(HuginBase::SrcPanoImage::CROP_RECTANGLE);
            };
            m_srcImg.setCropRect(cropRect);
        };

        // display in GUI
        m_focalLengthStr = hugin_utils::doubleTowxString(m_focalLength,2);
        m_focalLengthText->SetValue(m_focalLengthStr);
        m_cropFactorStr = hugin_utils::doubleTowxString(m_cropFactor,2);
        m_cropText->SetValue(m_cropFactorStr);
        m_HFOVStr = hugin_utils::doubleTowxString(m_HFOV,2);
        m_hfovText->SetValue(m_HFOVStr);
        SelectListValue(m_projChoice, m_srcImg.getProjection());

        // update lens type
        m_okButton->Enable();
    }
}


HuginBase::SrcPanoImage HFOVDialog::GetSrcImage()
{
    m_srcImg.setCropFactor(m_cropFactor);
    m_srcImg.setHFOV(m_HFOV);
    return m_srcImg;
}

double HFOVDialog::GetCropFactor()
{
    return m_cropFactor;
}

double HFOVDialog::GetFocalLength()
{
    return m_focalLength;
}

void HFOVDialog::OnOk(wxCommandEvent & e)
{
    if (m_srcImg.getProjection() == HuginBase::SrcPanoImage::FISHEYE_ORTHOGRAPHIC && m_HFOV>190)
    {
        if (hugin_utils::HuginMessageBox(
            wxString::Format(_("You have given a field of view of %.2f degrees.\n But the orthographic projection is limited to a field of view of 180 degress.\nDo you want still use that high value?"), m_HFOV),
            _("Hugin"), wxICON_EXCLAMATION | wxYES_NO, this) == wxNO)
        {
            return;
        };
    };
    EndModal(wxID_OK);
};