//
// Copyright (C) 1999-2004 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <WScom.h>
#include <WSDnwDev.h>
#include <WSDdraw.h>
#include <WSCbase.h>

WSMFclassInit(WSDnwDev,WSDdev);

WSDnwDev::WSDnwDev(){
  _use_pixmap_ = False;
  _no_child = True;
}

short     WSDnwDev::_copy_pixmap_x = 0;
short     WSDnwDev::_copy_pixmap_y = 0;
WSCushort WSDnwDev::_copy_pixmap_w = 0;
WSCushort WSDnwDev::_copy_pixmap_h = 0;

WSCushort WSDnwDev::_pixmap_w = 0;
WSCushort WSDnwDev::_pixmap_h = 0;
short WSDnwDev::_back_x = 0;
short WSDnwDev::_back_y = 0;

void WSDnwDev::getBackXY(short* x,short* y){
  *x = _back_x;
  *y = _back_y;
}
void WSDnwDev::getPixmapWH(WSCushort* h,WSCushort* w){
  *w = _pixmap_w;
  *h = _pixmap_h;
}
void WSDnwDev::getCopyPixmapGeom(short* x,short* y,WSCushort* w,WSCushort* h){
  *x = _copy_pixmap_x;
  *y = _copy_pixmap_y;
  *w = _copy_pixmap_w;
  *h = _copy_pixmap_h;
}

long WSDnwDev::setValue(long kind,void* data){
  char val;
  switch(kind){
    case WSDEV_USE_PIXMAP:
      val = *(char*)data;
      setUsePixmap(val);
      break;
    case WSDEV_SCALE_OFFSET:
      setScalePtr((double*)data);
      break;
    case WSDEV_X_OFFSET:
      setXOffsetPtr((short*)data);
      break;
    case WSDEV_Y_OFFSET:
      setYOffsetPtr((short*)data);
      break;

  }
  return WS_NO_ERR;
}

WSCbool WSDnwDev::getReady(){
  long d1 = getDeviceResource();
  long d2 = getWindowResource();
  long d3 = getContextResource();
  if (d1 == -1 || d2 == -1 || d3 == -1){
     return False;
  }
  return True;
}
long WSDnwDev::getWindowResource(){
  WSDdev* pt = getParentDev();
  if (pt == NULL){
    return -1;
  }
  return pt->getWindowResource(); 
}
long WSDnwDev::getContextResource(){
  WSDdev* pt = getParentDev();
  if (pt == NULL){
    return -1;
  }
  return pt->getContextResource(); 
}
long WSDnwDev::getSpecialResource(){
  WSDdev* pt = getParentDev();
  if (pt == NULL){
    return WS_ERR;
  }
  return pt->getSpecialResource(); 
}
long WSDnwDev::getDeviceResource(){
  WSDdev* pt = getParentDev();
  if (pt == NULL){
    return -1;
  }
  return pt->getDeviceResource(); 
}
WSCbool WSDnwDev::isAvailableEvent(long ev_kind){
  long val = WSGFeventToMaskBit(ev_kind);
  if ( val & getAvailableEventBit() ){
    return True;
  }
  return False;
}

long WSDnwDev::beginDraw(short x,short y,WSCushort w,WSCushort h,WSCbool absolute,WSCbool scaling){
  WSDdraw* draw = getDraw();
  if (draw == NULL){
    return WS_ERR;
  }
  //getReady() calls getWindow() or getPixmap() 
  //getPixmap() creates pixmap with _w,_h that has no effect values
  _h = 1;
  _w = 1;
  if (getReady() == False){
    return WS_ERR;
  }

  _absolute_draw = absolute;
  if (scaling != False){
    x = (short)((x + getXOffset())*getScale());
    y = (short)((y + getYOffset())*getScale());
    w = (WSCushort)(w * getScale());
    h = (WSCushort)(h * getScale());
  }

  _expose_sequence_ch = False;
  WSDdev* pdev = getParentDev();
  if ( pdev == NULL ){
    return WS_ERR;
  }
//printf("WSDnwbase::beginDraw() cl=%s p=%s\n",getAttachedClient()->getInstanceName(),pdev->getAttachedClient()->getInstanceName());
  if (_absolute_draw != False){
    void* wdev = pdev->cast("WSDwindowDev");
    if (wdev != NULL){
      WSCuchar fl;
      pdev->getValue(WSDEV_EXPOSE_SEQUENCE,(void*)&fl);
      _expose_sequence_bak = fl;
      if (_expose_sequence_bak != False){
        fl = False;
        pdev->setValue(WSDEV_EXPOSE_SEQUENCE,(void*)&fl);
        _expose_sequence_ch = True;
      }
    }
  }
  pdev->beginDraw( x, y, w, h,absolute,False);

  if (getUsePixmap() == False){
    _x = x; 
    _y = y; 
  }else{
    _back_x = x;
    _back_y = y;
    _x = 0;
    _y = 0;
  }
  _w = w; 
  _h = h; 

  short ex,ey;
  WSCushort ew,eh;
  long ret =   getExposedArea(&ex,&ey,&ew,&eh,False);
  if (ret != WS_NO_ERR){
    return WS_ERR;
  }

  draw->beginDraw(this,getDeviceResource(),getWindowResource(),getContextResource());

  //Expose のかかった部分だけリージョンをはる
  short ex2 = ex + ew < x + w ? ex + ew : x + w; 
  short ey2 = ey + eh < y + h ? ey + eh : y + h; 
  ex = ex > x ? ex : x; 
  ey = ey > y ? ey : y; 
  ew = ex2 - ex;
  eh = ey2 - ey;

  if (getUsePixmap() == False){
    if (absolute == False){
      draw->setRegion(ex,ey,ew,eh);
    }else{
      draw->setRegion(x,y,w,h);
    }
  }else{
    if (absolute == False){
      _copy_pixmap_x = ex - x;
      _copy_pixmap_y = ey - y;
      _copy_pixmap_w = ew;
      _copy_pixmap_h = eh;
      draw->setRegion(0,0,w,h);
    }else{
      _copy_pixmap_x = 0;
      _copy_pixmap_y = 0;
      _copy_pixmap_w = w;
      _copy_pixmap_h = h;
      draw->setRegion(0,0,w,h);
    }
  }

  return WS_NO_ERR;
}

long WSDnwDev::endDraw(){
  WSDdraw* draw = getDraw();
  if (draw == NULL){
    _absolute_draw = False;
    return WS_ERR;
  }

  draw->endDraw();

  if (getUsePixmap() != False){
    _copy_pixmap_to_window();
  }

  WSDdev* pdev = getParentDev();
  if ( pdev != NULL ){
    pdev->endDraw();
    if (_absolute_draw != False && _expose_sequence_ch != False ){
      void* wdev = pdev->cast("WSDwindowDev");
      if (wdev != NULL){
	    WSCuchar fl = _expose_sequence_bak;
        pdev->setValue(WSDEV_EXPOSE_SEQUENCE,(void*)&fl);
      }
    }
  } 
  _absolute_draw = False;
  return WS_NO_ERR;
}
long WSDnwDev::exposeArea(long x,long y,WSCulong w,WSCulong h,WSCbool scaling){

  WSDdev* pdev = getEventParentDev();
  if ( pdev != NULL ){

    double scale = getScale();
    if (scaling != False && scale != 1){
      pdev->exposeArea((short)((x + getXOffset()) * scale),
                        (short)((y + getXOffset()) * scale),
                        (WSCushort)(w * scale),
                        (WSCushort)(h * scale),False);
    }else{
      pdev->exposeArea(x + getXOffset(),y + getYOffset(),w,h,False);
    }
  }
  return WS_NO_ERR;
}


long WSDnwDev::clearArea(long x,long y,WSCulong w,WSCulong h,WSCbool create_event,WSCbool scaling){

  WSDdev* pdev = getEventParentDev();
  if ( pdev != NULL ){
    WSCuchar back;
    if (_use_pixmap_ != False){
      pdev->getValue(WSDEV_PIXMAP_STYLE,(void*)&back);
      if (back != WS_DYNAMIC_PIXMAP){
        WSCuchar fl = WS_DYNAMIC_PIXMAP;
        pdev->setValue(WSDEV_PIXMAP_STYLE,(void*)&fl);
      }
    }
    double scale = getScale();
    if (scaling != False && scale != 1){
      pdev->clearArea((short)((x + getXOffset()) * scale),
                        (short)((y + getXOffset()) * scale),
                        (WSCushort)(w * scale),
                        (WSCushort)(h * scale),create_event,False);
    }else{
      pdev->clearArea(x + getXOffset(),y + getYOffset(),w,h,create_event,False);
    }
    if (_use_pixmap_ != False){
      if (back != WS_DYNAMIC_PIXMAP){
        pdev->setValue(WSDEV_PIXMAP_STYLE,(void*)&back);
      }
    }
  }
  return WS_NO_ERR;
}

long WSDnwDev::copyArea(long x,long y,WSCulong w,WSCulong h,long dx,long dy){
  WSDdev* pdev = getEventParentDev();
  if ( pdev != NULL ){
    double scale = getScale();
    if (scale != 1){
      pdev->copyArea((short)((x + getXOffset()) * scale),
                        (short)((y + getXOffset()) * scale),
                        (WSCushort)(w * scale),
                        (WSCushort)(h * scale),
                        (short)((dx + getXOffset()) * scale),
                        (short)((dy + getXOffset()) * scale));
    }else{
      pdev->copyArea(x + getXOffset(),y + getYOffset(),w,h,
                        dx + getXOffset(),dy + getYOffset() );
    }
  }
  return WS_NO_ERR;
}

void WSDnwDev::getDispAddr(short* ox,short* oy){
   short cx = 0,cy = 0;
   WSCbase* client = getAttachedClient();
   if (client == NULL){
     *ox = 0;
     *oy = 0;
     return;
   }

   if (client->existProperty(WSNx) == True){
     client->getPropertyV(WSNx,&cx);
   }
   if (client->existProperty(WSNy) == True){
     client->getPropertyV(WSNy,&cy);
   }

   short px = 0,py = 0;
   WSDdev* parent = getParentDev();
   if (parent != NULL){
     parent->getDispAddr(&px,&py);
   }
   short cx2 = (short)((cx + getXOffset()) * getScale());
   short cy2 = (short)((cy + getYOffset()) * getScale());
   short absolute_cx;
   short absolute_cy;

   parent->getAbsoluteAddr(cx2,cy2,&absolute_cx,&absolute_cy);
   *ox = px + absolute_cx;
   *oy = py + absolute_cy;
   return;
}

void WSDnwDev::getAbsoluteAddr(short x,short y,short* ox,short* oy){
  WSDdev* parent = getParentDev();
  *ox =0;
  *oy =0;
  if (parent != NULL){
    parent->getAbsoluteAddr(x,y,ox,oy);
  }
  *ox = (short)((*ox + getXOffset()) * getScale());
  *oy = (short)((*oy + getYOffset()) * getScale());
}

long WSDnwDev::setAbsoluteAddrChangeHandler(void(*hd)(WSDdev*,void*),void* data){
  WSDdev* parent = getParentDev();
  if (parent != NULL){
    parent->setAbsoluteAddrChangeHandler(hd,data);
  }
  return WS_NO_ERR;
}

WSCbool WSDnwDev::getUsePixmap(){
  if (_use_pixmap_ == False){
    return _use_pixmap_;
  }else{
    WSDdev* parent = getParentDev();
    if (parent == NULL){
      return _use_pixmap_;
    }
    void* wdev = parent->cast("WSDwindowDev");
    if (wdev != NULL ){
	  WSCuchar fl = 0;
	  parent->getValue(WSDEV_PIXMAP_STYLE,(void*)&fl);
	  if (fl != WS_DIRECT_WINDOW){
        return False;
	  }
    }
    return _use_pixmap_;
  }
}

void WSDnwDev::setUsePixmap(WSCbool fl){
  _use_pixmap_ = fl;
}


long WSDnwDev::setEventOrder(WSDdev* dev,char direction){
  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }
  return parent->setEventOrder(dev,direction); 
}

long WSDnwDev::setEvent(WSDdev* dev,WSCbool on_off){
  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }
  return parent->setEvent(dev,on_off); 
}

long WSDnwDev::cancelEvent(WSDdev* dev){
  if (getEventRegistered() == False){
    return WS_NO_ERR;
  }
  if (dev == this){
    setEventRegistered(False);
  }

  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }
  parent->cancelEvent(dev); 
  return WS_ERR;
}

long WSDnwDev::setEnableEventBit(long ev_bit){
  if (getEventRegistered() != False){
    if ( ev_bit & getAvailableEventBit() ){
      long evmask = getEventMaskBit();
      evmask |= ( ev_bit & getAvailableEventBit() ); 
      setEventMaskBit( evmask );
    }
    return WS_NO_ERR;
  }
  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }
  if ( ev_bit & getAvailableEventBit() ){
    parent->setEvent(this,True);
    setEventMaskBit(  ev_bit & getAvailableEventBit() );
    setEventRegistered(True);
    return WS_NO_ERR;
  }
  return WS_ERR;
}

long WSDnwDev::setEnableEvent(long ev_kind){
  if (getEventRegistered() != False){
    if (isAvailableEvent(ev_kind) != False){
      long evmask = getEventMaskBit();
      evmask |= WSGFeventToMaskBit(ev_kind); 
      setEventMaskBit(evmask);
    }
    return WS_NO_ERR;
  }

  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }

  if (isAvailableEvent(ev_kind) != False){
    setEventRegistered(True);
    setEventMaskBit(WSGFeventToMaskBit(ev_kind));
    parent->setEvent(this,True);
    return WS_NO_ERR;
  }
  return WS_ERR;
}
long WSDnwDev::setDisableEvent(long ev_kind){
  long mask = getEventMaskBit();
  mask &= ~WSGFeventToMaskBit(ev_kind);
  setEventMaskBit(mask);

  WSDdev* parent = getParentDev();
  if (parent == NULL){
    return WS_ERR;
  }

  if (mask == 0){
    parent->setEvent(this,False);
    return WS_NO_ERR;
  }

  return WS_ERR;
}

long WSDnwDev::getAvailableEventBit(){
   return  ( WSEV_MOUSE_MOVE_BIT | WSEV_EXPOSE_BIT |
       WSEV_RESIZE_BIT    | WSEV_MOUSE_IN_BIT  |
       WSEV_MOUSE_OUT_BIT | WSEV_MOUSE_PRESS_BIT |
       WSEV_FOCUS_CH_BIT  | WSEV_MOUSE_RELEASE_BIT |
       WSEV_INITIALIZE_BIT | WSEV_VISIBLE_CH_BIT  |
       WSEV_KEY_PRESS_BIT  | WSEV_KEY_RELEASE_BIT |
       WSEV_KEY_HOOK_BIT );  
}

