RawTile TileManager::getTile( int resolution, int tile, int xangle, int yangle, int layers, CompressionType ctype ){

  RawTile* rawtile = NULL;
  string tileCompression;
  string compName;


  // Time the tile retrieval
  if( loglevel >= 3 ) tile_timer.start();


  /* Try to get the encoded tile directly from our cache first.
     Otherwise decode one from the source image and add it to the cache
   */
  switch( ctype )
    {

    case JPEG:
      if( (rawtile = tileCache->getTile( image->getImagePath(), resolution, tile,
					  xangle, yangle, JPEG, compressor->getQuality() )) ) break;
      if( (rawtile = tileCache->getTile( image->getImagePath(), resolution, tile,
					 xangle, yangle, UNCOMPRESSED, 0 )) ) break;
      break;


    case PNG:
      if( (rawtile = tileCache->getTile( image->getImagePath(), resolution, tile,
					 xangle, yangle, PNG, compressor->getQuality() )) ) break;
      if( (rawtile = tileCache->getTile( image->getImagePath(), resolution, tile,
					 xangle, yangle, UNCOMPRESSED, 0 )) ) break;
      break;


    case UNCOMPRESSED:
      if( (rawtile = tileCache->getTile( image->getImagePath(), resolution, tile,
					 xangle, yangle, UNCOMPRESSED, 0 )) ) break;
      break;


    default: 
      break;

    }



  if( loglevel >= 3 ){
    // Define our compression names for logging purposes
    switch( ctype ){
      case JPEG: compName = "JPEG"; break;
      case PNG: compName = "PNG"; break;
      case DEFLATE: compName = "DEFLATE"; break;
      case UNCOMPRESSED: compName = "UNCOMPRESSED"; break;
      default: break;
    }
  }


  // If we haven't been able to get a tile, get a raw one
  if( !rawtile || (rawtile && (rawtile->timestamp < image->timestamp)) ){

    if( rawtile && (rawtile->timestamp < image->timestamp) ){
      if( loglevel >= 3 ) *logfile << "TileManager :: Tile has old timestamp "
			           << rawtile->timestamp << " - " << image->timestamp
                                   << " ... updating" << endl;
    }

    if( loglevel >= 4 ) *logfile << "TileManager :: Cache Miss for resolution: " << resolution
				 << ", tile: " << tile
				 << ", compression: " << compName
				 << ", quality: " << compressor->getQuality() << endl
				 << "TileManager :: Cache Size: " << tileCache->getNumElements()
				 << " tiles, " << tileCache->getMemorySize() << " MB" << endl;

    
    RawTile newtile = this->getNewTile( resolution, tile, xangle, yangle, layers, ctype );

    if( loglevel >= 3 ) *logfile << "TileManager :: Total Tile Access Time: "
				 << tile_timer.getTime() << " microseconds" << endl;
    return newtile;
  }




  if( loglevel >= 3 ) *logfile << "TileManager :: Cache Hit for resolution: " << resolution
			       << ", tile: " << tile
			       << ", compression: " << compName
			       << ", quality: " << compressor->getQuality() << endl
			       << "TileManager :: Cache Size: "
			       << tileCache->getNumElements() << " tiles, "
			       << tileCache->getMemorySize() << " MB" << endl;


  // Check whether the compression used for out tile matches our requested compression type. If not, we must convert
  // Perform JPEG compression iff we have an 8 bit per channel image and either 1 or 3 bands
  // PNG compression can have 8 or 16 bits and alpha channels
  if( (rawtile->compressionType == UNCOMPRESSED) &&
      ( ( ctype==JPEG && rawtile->bpc==8 && (rawtile->channels==1 || rawtile->channels==3) ) || ctype==PNG ) ){

    // Rawtile is a pointer to the cache data, so we need to create a copy of it in case we compress it
    RawTile ttt( *rawtile );

    // Crop if this is an edge tile
    if( ( (ttt.width != image->getTileWidth()) || (ttt.height != image->getTileHeight()) ) && ttt.padded ){
      if( loglevel >= 5 ) * logfile << "TileManager :: Cropping tile" << endl;
      this->crop( &ttt );
    }

    if( loglevel >=2 ) compression_timer.start();
    unsigned int oldlen = rawtile->dataLength;
    unsigned int newlen = compressor->Compress( ttt );
    if( loglevel >= 3 ) *logfile << "TileManager :: " << compName << " requested, but UNCOMPRESSED compression found in cache." << endl
				 << "TileManager :: " << compName << " Compression Time: "
				 << compression_timer.getTime() << " microseconds" << endl
				 << "TileManager :: Compression Ratio: " << newlen << "/" << oldlen << " = "
				 << ( (float)newlen/(float)oldlen ) << endl;

    // Add our compressed tile to the cache
    if( loglevel >= 3 ) insert_timer.start();
    tileCache->insert( ttt );
    if( loglevel >= 3 ) *logfile << "TileManager :: Tile cache insertion time: " << insert_timer.getTime()
				 << " microseconds" << endl;

    if( loglevel >= 3 ) *logfile << "TileManager :: Total Tile Access Time: "
				 << tile_timer.getTime() << " microseconds" << endl;
    return RawTile( ttt );
  }

  if( loglevel >= 3 ) *logfile << "TileManager :: Total Tile Access Time: "
			       << tile_timer.getTime() << " microseconds" << endl;

  return RawTile( *rawtile );


}