#include <stdio.h>
#include <xclass/utils.h>

#include "OXView.h"

#define ALWAYS_SCROLLED 1

//----------------------------------------------------------------------

OXViewDocFrame::OXViewDocFrame(OXWindow *parent, int x, int y,
                         unsigned int options, unsigned long back) :
  OXFrame(parent, x, y, options, back) {

  const OPicture *bckgnd_pix;

  XGCValues gcv;
  int mask = GCTile | GCFillStyle;

  bckgnd_pix = _client->GetPicture("back2.xpm");
  if (!bckgnd_pix)
    FatalError("Missing background pixmap: back2.xpm");

  SetBackgroundPixmap(bckgnd_pix->GetPicture());
  gcv.tile = _client->GetPicture("back2.xpm")->GetPicture();
  gcv.fill_style = FillTiled;
  _backgc = XCreateGC(GetDisplay(), _id, mask, &gcv);

  _tx = 0;
  _ty = 0;
}

OXViewDocFrame::~OXViewDocFrame() {
  Clear();
}

void OXViewDocFrame::Clear() {
  _document->Clear();
}

void OXViewDocFrame::Layout() {
  _document->Layout();
}

void OXViewDocFrame::DrawRegion(int x, int y, int w, int h) {
  XRectangle rect;
  XGCValues gcv;
  int mask = GCTileStipXOrigin | GCTileStipYOrigin;

  rect.x = x;
  rect.y = y;
  rect.width = w;
  rect.height = h;

  gcv.ts_x_origin = -_tx;
  gcv.ts_y_origin = -_ty;
  XChangeGC(GetDisplay(), _backgc, mask, &gcv);

  XFillRectangle(GetDisplay(), _id, _backgc, x, y, w, h);

  _document->DrawRegion(GetDisplay(), _id, -_tx, -_ty, &rect);
}

void OXViewDocFrame::HScrollWindow(int x) {
  XPoint points[4];
  int xsrc, ysrc, xdest, ydest;

  // These points are the same for both cases, so set them here.

  if (x == _tx)
    return;

  points[0].y = points[3].y = 0;
  points[1].y = points[2].y = _h;
  ysrc = ydest = 0;

  if (x < _tx) {
    // scroll left...
    xsrc = 0;
    xdest = _tx - x;
    // limit the destination to the window width
    if (xdest > _w) xdest = _w;
    // Fill in the points array with the bounding box of the area that
    // needs to be redrawn - that is, the area that is not copied.
    points[1].x = points[0].x = 0;
    points[3].x = points[2].x = xdest; // -1;
  } else {
    // scroll right...
    xdest = 0;
    xsrc = x - _tx;
    // limit the source to the window height
    if (xsrc > _w) xsrc = _w;
    // Fill in the points array with the bounding box of the area that
    // needs to be redrawn - that is, the area that is not copied.
    points[1].x = points[0].x = _w - xsrc; // +1;
    points[3].x = points[2].x = _w;
  }
  // Copy the scrolled region to its new position
  XCopyArea(GetDisplay(), _id, _id, _backgc, xsrc, ysrc, _w, _h, xdest, ydest);
  // Set the new origin
  _tx = x;
  DrawRegion(points[0].x, points[0].y,
             points[2].x - points[0].x, points[2].y - points[0].y);
}

void OXViewDocFrame::VScrollWindow(int y) {
  XPoint points[4];
  int xsrc, ysrc, xdest, ydest;

  // These points are the same for both cases, so set them here.

  if (y == _ty)
    return;

  points[0].x = points[3].x = 0;
  points[1].x = points[2].x = _w;
  xsrc = xdest = 0;

  if (y < _ty) {
    // scroll down...
    ysrc = 0;
    ydest = _ty - y;
    // limit the destination to the window height
    if (ydest > _h) ydest = _h;
    // Fill in the points array with the bounding box of the area that
    // needs to be redrawn - that is, the area that is not copied.
    points[1].y = points[0].y = 0;
    points[3].y = points[2].y = ydest; // -1;
  } else {
    // scroll up...
    ydest = 0;
    ysrc = y - _ty;
    // limit the source to the window height
    if (ysrc > _h) ysrc = _h;
    // Fill in the points array with the bounding box of the area that
    // needs to be redrawn - that is, the area that is not copied.
    points[1].y = points[0].y = _h - ysrc; // +1;
    points[3].y = points[2].y = _h;
  }
  // Copy the scrolled region to its new position
  XCopyArea(GetDisplay(), _id, _id, _backgc, xsrc, ysrc, _w, _h, xdest, ydest);
  // Set the new origin
  _ty = y;
  DrawRegion(points[0].x, points[0].y,
             points[2].x - points[0].x, points[2].y - points[0].y);
}

//----------------------------------------------------------------------

OXView::OXView(const OXWindow *parent, OViewDoc * d, int x, int y,
                       unsigned int options, unsigned long back) :
  OXCompositeFrame(parent, x, y, options) {

  SetLayoutManager(new OHorizontalLayout(this));

  _background = _client->GetColorByName("black");

  _document = d;
  _document->CreateCanvas(this);
  _canvas = _document->GetCanvas();

  _vscrollbar = new OXVScrollBar(this, 16, 16, CHILD_FRAME);
  _hscrollbar = new OXHScrollBar(this, 16, 16, CHILD_FRAME);

  AddFrame(_canvas, NULL);
  AddFrame(_hscrollbar, NULL);
  AddFrame(_vscrollbar, NULL);

  MapSubwindows();
}

OXView::~OXView() {
  delete _canvas;
  delete _hscrollbar;
  delete _vscrollbar;
}

int OXView::LoadFile(char *fname) {
  char wintitle[100];
  FILE * file = fopen(fname, "r");
  if (file) {
    _document->LoadFile(file);
    fclose(file);
    Layout();
    return True;
  }
  return False;
}

void OXView::Clear() {
  _canvas->Clear();
  Layout();
}

int OXView::ProcessMessage(long msg, long parm1, long parm2) {
  switch(GET_MSG(msg)) {
    case XC_HSCROLL:
      switch(GET_SUBMSG(msg)) {
        case SB_SLIDERTRACK:
        case SB_SLIDERPOS:
          _canvas->HScrollWindow(parm1);
          break;
      }
      break;
    case XC_VSCROLL:
      switch(GET_SUBMSG(msg)) {
        case SB_SLIDERTRACK:
        case SB_SLIDERPOS:
          _canvas->VScrollWindow(parm1);
          break;
      }
      break;
  }
  return True;
}

void OXView::AdjustScrollbars() {
  int tdw, tdh, tcw, tch;

  tch = _h - (_bw << 1);
  tcw = _w - (_bw << 1);
  tdw = _canvas->_document->GetWidth();
  tdh = _canvas->_document->GetHeight();

  if (_vscrollbar->IsMapped()) {
    tcw -= _vscrollbar->GetDefaultWidth();
    if (_hscrollbar->IsMapped()) {
      tch -= _hscrollbar->GetDefaultHeight();
      if (tcw > tdw) tdw = tcw;
      _hscrollbar->SetRange(tdw, tcw);
    }
    if (tch > tdh) tdh = tch;
    _vscrollbar->SetRange(tdh, tch);
  } else {
    if (_hscrollbar->IsMapped()) {
      if (tcw > tdw) tdw = tcw;
      _hscrollbar->SetRange(tdw, tcw);
    }
  }
}

void OXView::Layout() {
  int tcw, tch;

  _canvas->_h = (tch = _h - (_bw << 1)) - _hscrollbar->GetDefaultHeight();
  _canvas->_w = (tcw = _w - (_bw << 1)) - _vscrollbar->GetDefaultWidth();
  _canvas->Layout();
  
  if (((tch >= _canvas->_document->GetHeight()) ||
       (_scroll_options & NO_VSCROLL)) &&
      (!(_scroll_options & FORCE_VSCROLL))) {
    _vscrollbar->UnmapWindow();
    _canvas->_ty = 0;
    if (((tcw >= _canvas->_document->GetWidth()) ||
         (_scroll_options & NO_HSCROLL)) &&
        (!(_scroll_options & FORCE_HSCROLL))) {
      _hscrollbar->UnmapWindow();
      _canvas->_tx = 0;
    } else {
      tch -= _hscrollbar->GetDefaultHeight();
      _hscrollbar->MoveResize(_bw, _bw + tch,
                              tcw, _hscrollbar->GetDefaultHeight());
      _hscrollbar->MapWindow();
    }
  } else {
    tcw -= _vscrollbar->GetDefaultWidth();
    if (((tcw >= _canvas->_document->GetWidth()) ||
         (_scroll_options & NO_HSCROLL)) &&
        (!(_scroll_options & FORCE_HSCROLL))) {
      _hscrollbar->UnmapWindow();
      _canvas->_tx = 0;
    } else {
      tch -= _hscrollbar->GetDefaultHeight();
      _hscrollbar->MoveResize(_bw, _bw + tch,
                              tcw, _hscrollbar->GetDefaultHeight());
      _hscrollbar->MapWindow();
    }
    _vscrollbar->MoveResize(_bw + tcw, _bw,
                            _vscrollbar->GetDefaultHeight(), tch);
    _vscrollbar->MapWindow();
  }
  AdjustScrollbars();
  _canvas->MoveResize(_bw, _bw, tcw, tch);
}

void OXView::ScrollDown() {
  int pos = _canvas->_document->GetHeight() - _canvas->GetHeight();

  if (pos < 0) pos = 0;
  if (pos == _canvas->_ty)
    _canvas->DrawRegion(0 ,0 , _canvas->_w, _canvas->_h);
  _vscrollbar->SetPosition(pos);
}

void OXView::DrawBorder() {
  switch (_options & (SUNKEN_FRAME | RAISED_FRAME | DOUBLE_BORDER)) {
  case SUNKEN_FRAME | DOUBLE_BORDER:
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, _w-2, 0);
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, 0, _h-2);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, _w-3, 1);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, 1, _h-3);

    XDrawLine (GetDisplay(), _id, _hilightGC, 0, _h-1, _w-1, _h-1);
    XDrawLine (GetDisplay(), _id, _hilightGC, _w-1, _h-1, _w-1, 0);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  1, _h-2, _w-2, _h-2);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  _w-2, 1, _w-2, _h-2);
    break;

  default:
    OXCompositeFrame::DrawBorder();
    break;
  }
}
