// Copyright (C) 2018, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_sg_write_paper
#define tools_sg_write_paper

//  To traverse a scene graph and "export" it at various file formats
// by using various rendering actions.
//  For example by using gl2ps_action, handle the formats:
//    gl2ps_eps: gl2ps producing eps
//    gl2ps_ps:  gl2ps producing ps
//    gl2ps_pdf: gl2ps producing pdf
//    gl2ps_svg: gl2ps producing svg
//    gl2ps_tex: gl2ps producing tex
//    gl2ps_pgf: gl2ps producing pgf
// By using the zb_action (zb for zbuffer):
//    inzb_ps: tools::sg offscreen zbuffer put in a PostScript file.
//    inzb_png: zbuffer put in a png file. It needs to provide a "png_writer" function.
//    inzb_jpeg: zbuffer put in a jpeg file. It needs to provide a "jpeg_writer" function.

#include "zb_action"
#include "node"
#include "gl2ps_action"
#include "../wps"
#include "../typedefs"
#include "../touplow"

namespace tools {
namespace sg {

typedef bool (*png_writer)(std::ostream&,const std::string&,
                           unsigned char*,unsigned int,unsigned int,unsigned int);

typedef bool (*jpeg_writer)(std::ostream&,const std::string&,
                            unsigned char*,unsigned int,unsigned int,unsigned int,int);

inline bool write_paper(std::ostream& a_out,
                        gl2ps_manager& a_gl2ps_mgr,zb_manager& a_zb_mgr,
                        png_writer a_png_writer,jpeg_writer a_jpeg_writer,
                        float a_back_r,float a_back_g,float a_back_b,float a_back_a,
                        node& a_scene_graph,
                        unsigned int a_width,unsigned int a_height,
                        const std::string& a_file,const std::string& a__format,
                        bool a_do_transparency,
                        bool a_top_to_bottom,
                        const std::string& a_opts_1,const std::string& a_opts_2) {
  if(!a_width || !a_height) return false;
  std::string a_format = a__format;
  tolowercase(a_format);  //handle legacy.
  int gl2ps_format;
  if(gl2ps_s2format(a_format,gl2ps_format)) {
    int sort = -1;
    if(a_opts_1.size() && !gl2ps_s2sort(a_opts_1,sort)) {
      a_out << "tools::sg::write_paper: bad gl2ps sort " << sout(a_opts_1) << "." << std::endl;
      return false;
    }
    int options = -1;
    if(a_opts_2.size() && !gl2ps_s2options(a_opts_2,options)) {
      a_out << "tools::sg::write_paper: bad gl2ps options " << sout(a_opts_2) << "." << std::endl;
      return false;
    }
    gl2ps_action action(a_gl2ps_mgr,a_out,a_width,a_height);
    action.clear_color(a_back_r,a_back_g,a_back_b,a_back_a);
    if(!action.open(a_file,gl2ps_format,sort,options)) return false;
    action.set_do_transparency(false);
    action.set_have_to_do_transparency(false);
    a_scene_graph.render(action);
    if(!action.end()) { //check that matrices stack are ok.
      a_out << "tools::sg::write_paper: bad gl2ps_action end." << std::endl;
      action.close();
      return false;
    } else if(a_do_transparency) {
      if(action.have_to_do_transparency()) {
        action.set_do_transparency(true);
        a_scene_graph.render(action);
        if(!action.end()) { //check that matrices stack are ok.
          a_out << "tools::sg::write_paper: bad gl2ps_action end." << std::endl;
          action.close();
          return false;
        }
      }
    }
    action.close();
    return true;
  }

  zb_action action(a_zb_mgr,a_out,a_width,a_height);
  action.clear_color_buffer(a_back_r,a_back_g,a_back_b,a_back_a);
  action.clear_depth_buffer();
  action.set_do_transparency(false);
  action.set_have_to_do_transparency(false);
  a_scene_graph.render(action);
  if(!action.end()) { //check that matrices stack are ok.
    a_out << "tools::sg::write_paper: bad zb_action end." << std::endl;
    return false;
  } else if(a_do_transparency) {
    if(action.have_to_do_transparency()) {
      action.set_do_transparency(true);
      a_scene_graph.render(action);
      if(!action.end()) { //check that matrices stack are ok.
        a_out << "tools::sg::write_paper: bad zb_action end." << std::endl;
        return false;
      }
    }
  }

  if((a_format=="zb_ps")||(a_format=="inzb_ps")) {
    wps wps(a_out);
    if(!wps.open_file(a_file)) {
      a_out << "tools::sg::write_paper : can't open " << a_file << "." << std::endl;
      return false;
    }
    wps.PS_BEGIN_PAGE();
    wps.PS_PAGE_SCALE(float(a_width),float(a_height));
    wps.PS_IMAGE(a_width,a_height,wps::rgb_4,zb_action::get_rgb,&action);
    wps.PS_END_PAGE();
    wps.close_file();
    return true;
  }

  if((a_format=="zb_png")||(a_format=="inzb_png")) {
    if(!a_png_writer) {
      a_out << "tools::sg::write_paper : no png_writer given." << std::endl;
      return false;
    }

    std::vector<unsigned char> buffer;
    if(!action.get_rgbas(a_top_to_bottom,buffer)) {
      a_out << "tools::sg::write_paper : can't get rgba image." << std::endl;
      return false;
    }

    if(!a_png_writer(a_out,a_file,buffer.data(),a_width,a_height,4)) {
      a_out << "tools::sg::write_paper : tools::png::write() failed." << std::endl;
      return false;
    }
    return true;
  }

  if((a_format=="zb_jpeg")||(a_format=="zb_jpg")||(a_format=="inzb_jpeg")) {

    if(!a_jpeg_writer) {
      a_out << "tools::sg::write_paper : no jpeg_writer given." << std::endl;
      return false;
    }

    std::vector<unsigned char> buffer;
    if(!action.get_rgbs(a_top_to_bottom,buffer)) {
      a_out << "tools::sg::write_paper : can't get rgb image." << std::endl;
      return false;
    }

    if(!a_jpeg_writer(a_out,a_file,buffer.data(),a_width,a_height,3,100)) {
      a_out << "tools::sg::write_paper : tools::jpeg::write() failed." << std::endl;
      return false;
    }
    return true;
  }

  a_out << "tools::sg::write_paper : unknown format " << a_format << std::endl;
  return false;
}

inline bool write_paper(std::ostream& a_out,
                        gl2ps_manager& a_gl2ps_mgr,zb_manager& a_zb_mgr,
                        png_writer a_png_writer,jpeg_writer a_jpeg_writer,
                        const tools::colorf& a_back,
                        node& a_scene_graph,
                        unsigned int a_width,unsigned int a_height,
                        const std::string& a_file,const std::string& a_format,
                        bool a_do_transparency,bool a_top_to_bottom,
                        const std::string& a_opts_1,const std::string& a_opts_2) {
  return tools::sg::write_paper(a_out,a_gl2ps_mgr,a_zb_mgr,
                                a_png_writer,a_jpeg_writer,
                                a_back.r(),a_back.g(),a_back.b(),a_back.a(),
                                a_scene_graph,a_width,a_height,a_file,a_format,
                                a_do_transparency,a_top_to_bottom,a_opts_1,a_opts_2);
}

inline bool write_paper(std::ostream& a_out,
                        gl2ps_manager& a_gl2ps_mgr,zb_manager& a_zb_mgr,
                        png_writer a_png_writer,jpeg_writer a_jpeg_writer,
                        const tools::colorf& a_back,
                        node& a_scene_graph,
                        unsigned int a_width,unsigned int a_height,
                        const std::string& a_file,const std::string& a_format) {
  return tools::sg::write_paper(a_out,a_gl2ps_mgr,a_zb_mgr,
                                a_png_writer,a_jpeg_writer,
                                a_back.r(),a_back.g(),a_back.b(),a_back.a(),
                                a_scene_graph,a_width,a_height,a_file,a_format,
                                true,true,std::string(),std::string());
}

}}

#endif
