
#include "QGraphicImage256.h"

#include <QPainter.h>
#include <QGraphicsItem>
#include <QGraphicsItemGroup>

#include <QImage>
#include <QGraphicsSceneMouseEvent>
#include <QCursor>

#include <QDebug>

class RdrDisplayCx
{
public:
  bool showOcclusion;
};

static RdrDisplayCx cx;

class QGraphicImage256 : public QGraphicsItem
{
public:
  QImage occlusionImage;
  QImage image;
  QImage imageBg;
  QImage img32;
  QRectF myRect;
  unsigned long long pal[256];

  int iw;
  int ih;
  bool gg_updated;

  typedef unsigned char video_memory_t[1024][1024];
  const video_memory_t* video_memory;

  QGraphicImage256(int w, int h);

  QRectF boundingRect() const;

  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

  void mouseMoveEvent(QGraphicsSceneMouseEvent* evn);

  void set_color(int i, unsigned long v)
  {
    image.setColor(i, v);
    pal[i]=v;
  }
};


QGraphicImage256::QGraphicImage256(int w, int h):
  occlusionImage(w, h, QImage::Format_ARGB32),
  image(1024, 1024, QImage::Format_Indexed8),
  imageBg(1024, 1024, QImage::Format_Indexed8),
  img32(w, h, QImage::Format_ARGB32),
  iw(w),
  ih(h),
  gg_updated(true)
{
  occlusionImage.fill(Qt::darkCyan);
  image.setColorCount(256);
  imageBg.setColorCount(256);
  {
      for(int i=0; i<1024; ++i)
      {
        unsigned long* p=reinterpret_cast<unsigned long*>(imageBg.scanLine(i));
        for(int j=0; j<1024/4; ++j)
        {
            unsigned long v=((j+i) % 16);
            v=v|(v<<8);
            v=v|(v<<16);
            v=v<<4;
            p[j]=v;
        }
      }
  }

  set_color(0, qRgba(0, 0, 0, 0)); //image.setColor(0, qRgba(0, 0, 0, 0));
  set_color(1, qRgba(0, 0, 0, 0)); //image.setColor(0, qRgba(0, 0, 0, 0));
  for(int i=1; i<256; ++i)
  {
      set_color(i, qRgba(0x80, 0x80, 0x80, 255)); //image.setColor(0, qRgba(0, 0, 0, 0));//image.setColor(i, qRgb(i, i, i));
  }
  image.fill(0);
  /*
  for(int i=19; i<31; ++i)
  {
    image.setPixel(i, 19, 1);
    image.setPixel(i, 30, 1);
    image.setPixel(19, i, 1);
    image.setPixel(30, i, 1);
  }
  for(int i=20; i<30; ++i)
    for(int j=20; j<30; ++j)
      image.setPixel(i, j, 200);
  */
  setCursor(Qt::CrossCursor);
  setSelected(true);
  myRect=QRectF(0, 0, w, h); //512, 512);

  video_memory=(video_memory_t*)image.bits();
}

QRectF QGraphicImage256::boundingRect() const
{
  return  myRect;
}

void QGraphicImage256::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
  if (gg_updated)
  {
#if 0
    img32=image.convertToFormat(QImage::Format_ARGB32);
#else
#if 0
    if (cx.showOcclusion)
    {
        for(int i=0; i<ih; ++i)
        {
          unsigned long long* p0=reinterpret_cast<unsigned long long*>(imageBg.scanLine(i));
          unsigned long long* p1=reinterpret_cast<unsigned long long*>(image.scanLine(i));
          for(int j=0; j<(iw/8)+1; ++j)
          {
              unsigned long long tmp=p1[j];
              p1[j]=tmp+p0[j];//+p1[j];
          }
        }
    }
#endif
    for(int r=0; r<ih; ++r)
    {
      unsigned int* scan_line=(unsigned int*)img32.scanLine(r);
      const unsigned char* src=(*video_memory)[r];
      for(int c=0; c<iw; ++c)
      {
        unsigned char c8=src[c];
        unsigned int c32=pal[c8];
#if 0
        unsigned int c32=0; //c8==0 ? 0 : (255<<24)|(c8<<16)|(c8<<8)|c8;
        //c8=c8<<4;
        if (c8==1)
          c32=qRgba(80, 0, 0, 255);
        else if (c8>1)
          c32=pal[c8] | 0xFF000000U; //qRgba(128, 128, 128, 255);
#endif
        scan_line[c]=c32;
      }
    }
#endif
    gg_updated=false;
  }

  if (cx.showOcclusion)
    painter->drawImage(0, 0, occlusionImage);
  else
    painter->setCompositionMode(QPainter::CompositionMode_Source);

  painter->drawImage(0, 0, img32, 0, 0, iw, ih);
}

void QGraphicImage256::mouseMoveEvent(QGraphicsSceneMouseEvent* evn)
{
  QPointF pos=evn->pos();
  //Q_EMIT sigCursorCoordinatesChangedX(pos.x());
}


class RdrGraphicsScene::Implementation
{
public:
  int sw;
  int sh;
  QGraphicImage256* rdrImage;
  //QGraphicsItemGroup* gDbg;
  QGraphicsRectItem* gP1;
  QGraphicsEllipseItem* gP2;
  QGraphicsLineItem* gL;

  QPoint crsPos;
  QPoint dPos;
};

RdrGraphicsScene::RdrGraphicsScene(int w, int h):
  p_(*new Implementation)
{
  p_.sw=w;
  p_.sh=h;
  p_.rdrImage=new QGraphicImage256(w, h);
  //p_.gDbg=new QGraphicsItemGroup;
  //p_.gDbg->setOpacity(0.4);

  p_.gP1=new QGraphicsRectItem(-2, -2, 5, 5);
  p_.gP2=new QGraphicsEllipseItem(-2, -2, 5, 5);
  p_.gP1->setPos(10, 10);
  p_.gP2->setPos(10, 10);
  p_.gL=new QGraphicsLineItem(p_.gP1->pos().x(), p_.gP1->pos().y(), p_.gP2->pos().x(), p_.gP2->pos().y());

  p_.gP1->setVisible(false);
  //p_.gP1->setFlag(QGraphicsItem::ItemIsSelectable, true);
  //p_.gP1->setFlag(QGraphicsItem::ItemIsMovable, true);
  p_.gP1->setBrush(Qt::cyan);
  p_.gP1->setPen(QPen(Qt::cyan));
  p_.gP2->setVisible(false);
  p_.gP2->setBrush(Qt::cyan);
  p_.gP2->setPen(QPen(Qt::cyan));
  p_.gL->setVisible(false);
  p_.gL->setPen(QPen(Qt::cyan));

  p_.gL->setOpacity(0.4);
  p_.gP1->setOpacity(0.4);
  p_.gP2->setOpacity(0.4);

  //QTransform trans;
  //trans.translate(-3, -3);
  //p_.gP1->setTransform(trans);
  //p_.gP2->setTransform(trans);

  addItem(p_.rdrImage);
  addItem(p_.gP1);
  addItem(p_.gP2);
  addItem(p_.gL);
}

void* RdrGraphicsScene::video_memory(void) const
{
  return p_.rdrImage->image.bits();
}

void RdrGraphicsScene::ggUpdateImage()
{
  p_.rdrImage->gg_updated=true;
}

const QImage& RdrGraphicsScene::getImage() const
{
    return p_.rdrImage->img32;
}

void RdrGraphicsScene::saveSnapshot(const QString fname)
{
    p_.rdrImage->img32.save(fname);
}

void RdrGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* evn)
{
  QPointF pos=evn->scenePos();

  p_.crsPos=QPoint(pos.x(), pos.y());
  //p_.dPos=p_.gP1->pos(); //-p_.crsPos;

  //Q_EMIT sigCursorX(pos.x());
  //Q_EMIT sigCursorY(pos.y());

  Q_EMIT sigCursorXY(pos.x(), pos.y(), evn->modifiers());

  QGraphicsScene::mouseMoveEvent(evn);
}

void RdrGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  static int click_state=0;

  QPointF pos=event->scenePos();

  p_.crsPos=QPoint(pos.x(), pos.y());

  if (event->modifiers()==Qt::NoModifier)
  {
    if (event->button()==Qt::LeftButton)
    {
      //p_.gDbg->setVisible(true);
      p_.dPos=QPoint(pos.x(), pos.y());
      if (click_state==0)
      {
        p_.gP1->setVisible(true);
        p_.gP1->setPos(pos.x(), pos.y());
        click_state=0;
      }
      else
      {
        p_.gP2->setVisible(true);
        p_.gP2->setPos(pos.x(), pos.y());
        p_.gL->setVisible(true);
        click_state=0;
      }
      //p_.gL->setLine(p_.gP1->pos().x(), p_.gP1->pos().y(), p_.gP2->pos().x(), p_.gP2->pos().y());
      Q_EMIT sigLeftClick(pos.x(), pos.y());
    }
  }
  QGraphicsScene::mousePressEvent(event);
}

void RdrGraphicsScene::ggUpdatePalet(unsigned long* pal)
{
  for(int i=0; i<256; ++i)
    p_.rdrImage->set_color(i, pal[i]);
}

void RdrGraphicsScene::ggUpdatePalet(unsigned int index, unsigned long v)
{
    if (index<256)
      p_.rdrImage->set_color(index, v);
}

void RdrGraphicsScene::setBg(bool b)
{
  cx.showOcclusion=b;
  update();
}

QRect RdrGraphicsScene::dbgDeltaPos() const
{
    return QRect(p_.crsPos, p_.dPos);
}
