struct IsoParam : Object {
  int floor, wall, icon, iconh;
  int sx, sy, flev;
  };

struct TileIsoCache : Tile {
  int orig;
  int sf;
  int param;
  int cache, cachechg;
  int hash() const {
    return (orig ^ (sf * 13157) ^ (param * 71)) & HASHMAX;
    }
  };

bool eq(const TileIsoCache& t1, const TileIsoCache& t2) {
  return t1.orig == t2.orig && t1.sf == t2.sf && t1.sf == t2.sf;
  }

int cfix(int col, int mul) {
  return (col & 0xFF000000) + ((col & 0xFCFCFC) >> 2) * mul;
  }

int renderAsIso(int ch, int sf, IsoParam *P) {
  Get(TileImage, TI, ch);

  Get(TileFill, TF, ch);
  if(TF) {
    static Image i;
    i.s = exsurface;
    static TileImage ti;
    ti.ox = 0; ti.oy = 0; ti.sx = 1, ti.sy = 1; ti.i = &i;
    ti.trans = TF->alpha == 0xFFFFFF ? -1 : transAlpha;
    qpixel(exsurface, 0, 0) = TF->color + ((TF->alpha&0xFF) << 24);
    TI = &ti;
    }
    
  if(TI) {
    TileIsoCache tic;
    tic.orig = ch;
    tic.sf = sf;
    tic.param = P->id;
    tic.cache = 0;
    int rtile = registerTile(tic);
    Get(TileIsoCache, TIC, rtile);
    
    if(TIC->cache && TIC->cachechg != TI->i->changes) {
      Get(TileTransform, TT, TIC->cache);
      if(TT) return TIC->cache; // never invalidated
      // invalidate cache
      Get(TileImage, TIO, TIC->cache);
      if(TIO) {
        // it is not listed in the objects list,
        // so we simply delete it
        // deleteobj(TIO->i->id);
        delete TIO->i; 
        deleteobj(TIC->cache);
        }
      TIC->cache = 0;
      }
    
    if(!TIC->cache) {
    
      int newback  = TI->trans == transAlpha ? 0 : TI->trans;
      int newtrans = TI->trans;
      
      TIC->cache = 0;

      // special case...
      if(sf == spIItem) {
        TIC->cache = addTransform(ch, 
           1440*(P->sx-P->icon)/2/P->sx,
           1440*(P->flev-P->iconh)/P->sy,
           1440 * P->icon / P->sx,
           1440 * P->iconh / P->sy,
            0,0
            );
        return TIC->cache;
        }

      Image *i = new Image(P->sx, P->sy, newback);
      //for(int x=0; x<P->sx; x++) for(int y=0; y<P->sy; y++) qpixel(i->s, x, y) = -1;
      
      if(sf & spIFloor) {
        for(int ty=0; ty<P->floor; ty++)
        for(int tx=0; tx<P->floor; tx++) {
          int opix = qpixel(TI->i->s, TI->ox + (tx*TI->sx)/P->floor, TI->oy + (ty*TI->sy)/P->floor);
          if(!istrans(opix, TI->trans))
            qpixel(i->s, P->sx/2+tx-ty, P->flev+((tx+ty-P->floor)>>1)) = opix;
          }
        }
      
      if(sf & spIItem) {
        for(int ty=0; ty<P->iconh; ty++)
        for(int tx=0; tx<P->icon; tx++) {
          int orx = tx * TI->sx / P->icon;
          int ory = ty * TI->sy / P->iconh;
          int opix = qpixel(TI->i->s, TI->ox + orx, TI->oy + ory);
          if(!istrans(opix, TI->trans)) {
            int ix = tx + (P->sx-P->icon)/2;
            int iy = ty + P->flev-P->iconh;
            qpixel(i->s, ix, iy) = opix;
            }
          }
        }

      if(sf & spIWallL) {
        for(int ty=0; ty<P->wall; ty++)
        for(int tx=0; tx<P->floor; tx++) {
          int opix = qpixel(TI->i->s, TI->ox + (tx*TI->sx)/P->floor, TI->oy + (ty*TI->sy)/P->wall);
          if(!istrans(opix, TI->trans))
            qpixel(i->s, P->sx/2-P->floor+tx, P->flev+ty+(tx/2)-P->wall) = cfix(opix, 2);
          }
        }

      if(sf & spIWallR) {
        for(int ty=0; ty<P->wall; ty++)
        for(int tx=0; tx<P->floor; tx++) {
          int opix = qpixel(TI->i->s, TI->ox + (tx*TI->sx)/P->floor, TI->oy + (ty*TI->sy)/P->wall);
          if(!istrans(opix, TI->trans))
            qpixel(i->s, P->sx/2+tx, P->flev+P->floor/2+ty-(tx/2)-P->wall-1) = cfix(opix, 3);
          }
        }

      if(sf & spICeil) {
        for(int ty=0; ty<P->floor; ty++)
        for(int tx=0; tx<P->floor; tx++) {
          int opix = qpixel(TI->i->s, TI->ox + (tx*TI->sx)/P->floor, TI->oy + (ty*TI->sy)/P->floor);
          if(!istrans(opix, TI->trans))
            qpixel(i->s, P->sx/2+tx-ty, P->flev-P->wall+((tx+ty-P->floor)>>1)) = opix;
          }
        }

      TIC->cache = addTile(i, 0, 0, P->sx, P->sy, newtrans);
      TIC->cachechg = TI->i->changes;
      }      

    return TIC->cache;
    }
  
  Get(TileMerge, TM, ch);
  if(TM) {
    return addMerge(renderAsIso(TM->t1, sf, P), renderAsIso(TM->t2, sf, P), TM->over);
    }
  
  Get(TileSpatial, TS, ch);
  if(TS) {
    if(sf & TS->sf)
      return renderAsIso(TS->t1, sf & TS->sf, P);
    else return 0;
    }

  Get(TileLayer, TL, ch);
  if(TL) {
    return addLayer(renderAsIso(TL->t1, sf, P), TL->layerid);
    }

  Get(TileRecolor, TR, ch);
  if(TR) {
    int ds = renderAsIso(TR->t1, sf, P);
    return addRecolor(ds, TR->color, TR->mode);
    }
  
  Get(TileTransform, TT, ch);
  if(TT) {
    IsoParam P2 = *P;
    //int idx = (TT->dx*P->icon + (P->sx-P->icon)/2 * (1440-TT->sx)) / P->sx / 1440;
    int shx = (P->sx-P->icon)/2;
    int shy = P->flev-P->iconh;
    int idx = (TT->dx*P->icon + shx * (1440-TT->sx)) / P->sx;
    int idy = (TT->dy*P->iconh + shy * (1440-TT->sx)) / P->sy;
    return addTransform(
      renderAsIso(TT->t1, sf, &P2),
      idx, idy,
      TT->sx, TT->sy,
      0,0
      );
    }

  return 0;
  }

#ifdef USELUA
int lh_isoparam(lua_State *L) {
  checkArg(L, 4, "isoparam");
  
  IsoParam *P = new IsoParam;
  P->floor = luaInt(1);
  P->wall  = luaInt(2);
  P->icon  = luaInt(3);
  P->iconh = luaInt(4);
  
  P->sx = max(P->icon, P->floor*2);
  P->flev = max(P->iconh, P->floor/2 + P->wall);
  P->sy = (P->floor/2) + P->flev;
  
  return retObject(L, P);
  }

int lh_isosizes(lua_State *L) {
  checkArg(L, 1, "isosizes");
  IsoParam *P = luaO(1, IsoParam);
  lua_newtable(L);
  setfield(L, "floor", P->floor);
  setfield(L, "x", P->sx);
  setfield(L, "y", P->sy);
  return 1;
  }

int lh_iso(lua_State *L) {
  checkArg(L, 2, "isoproject");
  
  return retInt(L, renderAsIso(luaInt(1), spIFloor | spIItem | spIWallL | spIWallR | spICeil, luaO(2, IsoParam)));
  }
#endif
