/**
   * Get mean covariance matrix C2 for given pixel.
   *
   * @param x X coordinate of the given pixel.
   * @param y Y coordinate of the given pixel.
   * @param halfWindowSizeX The sliding window width /2
   * @param halfWindowSizeY The sliding window height /2
   * @param sourceImageWidth Source image width.
   * @param sourceImageHeight Source image height.
   * @param sourceProductType The source product type.
   * @param sourceTiles The source tiles for all bands.
   * @param dataBuffers Source tile data buffers.
   * @param Cr The real part of the mean covariance matrix.
   * @param Ci The imaginary part of the mean covariance matrix.
   */
  public static void getMeanCovarianceMatrixC2(
      final int x,
      final int y,
      final int halfWindowSizeX,
      final int halfWindowSizeY,
      final int sourceImageWidth,
      final int sourceImageHeight,
      final PolBandUtils.MATRIX sourceProductType,
      final Tile[] sourceTiles,
      final ProductData[] dataBuffers,
      final double[][] Cr,
      final double[][] Ci) {

    final double[][] tempCr = new double[2][2];
    final double[][] tempCi = new double[2][2];

    final int xSt = Math.max(x - halfWindowSizeX, 0);
    final int xEd = Math.min(x + halfWindowSizeX, sourceImageWidth - 1);
    final int ySt = Math.max(y - halfWindowSizeY, 0);
    final int yEd = Math.min(y + halfWindowSizeY, sourceImageHeight - 1);
    final int num = (yEd - ySt + 1) * (xEd - xSt + 1);

    final TileIndex srcIndex = new TileIndex(sourceTiles[0]);

    final Matrix CrMat = new Matrix(2, 2);
    final Matrix CiMat = new Matrix(2, 2);

    if (sourceProductType == PolBandUtils.MATRIX.C2) {

      for (int yy = ySt; yy <= yEd; ++yy) {
        srcIndex.calculateStride(yy);
        for (int xx = xSt; xx <= xEd; ++xx) {
          getCovarianceMatrixC2(srcIndex.getIndex(xx), dataBuffers, tempCr, tempCi);
          CrMat.plusEquals(new Matrix(tempCr));
          CiMat.plusEquals(new Matrix(tempCi));
        }
      }

    } else if (sourceProductType == PolBandUtils.MATRIX.LCHCP
        || sourceProductType == PolBandUtils.MATRIX.RCHCP
        || sourceProductType == PolBandUtils.MATRIX.DUAL_HH_HV
        || sourceProductType == PolBandUtils.MATRIX.DUAL_VH_VV
        || sourceProductType == PolBandUtils.MATRIX.DUAL_HH_VV) {

      final double[] tempKr = new double[2];
      final double[] tempKi = new double[2];

      for (int yy = ySt; yy <= yEd; ++yy) {
        srcIndex.calculateStride(yy);
        for (int xx = xSt; xx <= xEd; ++xx) {
          getScatterVector(srcIndex.getIndex(xx), dataBuffers, tempKr, tempKi);
          computeCovarianceMatrixC2(tempKr, tempKi, tempCr, tempCi);
          CrMat.plusEquals(new Matrix(tempCr));
          CiMat.plusEquals(new Matrix(tempCi));
        }
      }

    } else {
      throw new OperatorException("getMeanCovarianceMatrixC2 not implemented for raw dual pol");
    }

    CrMat.timesEquals(1.0 / num);
    CiMat.timesEquals(1.0 / num);
    for (int i = 0; i < 2; i++) {
      Cr[i][0] = CrMat.get(i, 0);
      Ci[i][0] = CiMat.get(i, 0);

      Cr[i][1] = CrMat.get(i, 1);
      Ci[i][1] = CiMat.get(i, 1);
    }
  }
  /**
   * Called by the framework in order to compute a tile for the given target band.
   *
   * <p>The default implementation throws a runtime exception with the message "not implemented".
   *
   * @param targetBand The target band.
   * @param targetTile The current tile associated with the target band to be computed.
   * @param pm A progress monitor which should be used to determine computation cancelation
   *     requests.
   * @throws org.esa.snap.framework.gpf.OperatorException If an error occurs during computation of
   *     the target raster.
   */
  public void computeTile(Band targetBand, Tile targetTile, ProgressMonitor pm)
      throws OperatorException {

    final Rectangle targetTileRectangle = targetTile.getRectangle();
    final int x0 = targetTileRectangle.x;
    final int y0 = targetTileRectangle.y;
    final int w = targetTileRectangle.width;
    final int h = targetTileRectangle.height;

    Tile sourceRaster1 = null;
    ProductData srcData1 = null;
    ProductData srcData2 = null;
    Band sourceBand1 = null;

    final String[] srcBandNames = targetBandNameToSourceBandName.get(targetBand.getName());
    if (srcBandNames.length == 1) {
      sourceBand1 = sourceProduct.getBand(srcBandNames[0]);
      sourceRaster1 = calibrationOp.getSourceTile(sourceBand1, targetTileRectangle);
      srcData1 = sourceRaster1.getDataBuffer();
    } else {
      sourceBand1 = sourceProduct.getBand(srcBandNames[0]);
      final Band sourceBand2 = sourceProduct.getBand(srcBandNames[1]);
      sourceRaster1 = calibrationOp.getSourceTile(sourceBand1, targetTileRectangle);
      final Tile sourceRaster2 = calibrationOp.getSourceTile(sourceBand2, targetTileRectangle);
      srcData1 = sourceRaster1.getDataBuffer();
      srcData2 = sourceRaster2.getDataBuffer();
    }

    final Unit.UnitType bandUnit = Unit.getUnitType(sourceBand1);

    // copy band if unit is phase
    if (bandUnit == Unit.UnitType.PHASE) {
      targetTile.setRawSamples(sourceRaster1.getRawSamples());
      return;
    }

    final ProductData trgData = targetTile.getDataBuffer();
    final TileIndex srcIndex = new TileIndex(sourceRaster1);
    final TileIndex tgtIndex = new TileIndex(targetTile);

    final int maxY = y0 + h;
    final int maxX = x0 + w;

    double dn = 0, dn2 = 0, sigma, i, q;
    int srcIdx, tgtIdx;

    for (int y = y0; y < maxY; ++y) {
      srcIndex.calculateStride(y);
      tgtIndex.calculateStride(y);

      for (int x = x0; x < maxX; ++x) {
        srcIdx = srcIndex.getIndex(x);
        tgtIdx = tgtIndex.getIndex(x);

        if (bandUnit == Unit.UnitType.AMPLITUDE) {
          dn = srcData1.getElemDoubleAt(srcIdx);
          dn2 = dn * dn;
        } else if (bandUnit == Unit.UnitType.INTENSITY) {
          dn2 = srcData1.getElemDoubleAt(srcIdx);
        } else if (bandUnit == Unit.UnitType.REAL || bandUnit == Unit.UnitType.IMAGINARY) {
          if (outputImageInComplex) {
            dn = srcData1.getElemDoubleAt(srcIdx);
          } else {
            i = srcData1.getElemDoubleAt(srcIdx);
            q = srcData2.getElemDoubleAt(srcIdx);
            dn2 = i * i + q * q;
          }
        } else {
          throw new OperatorException("ALOS Calibration: unhandled unit");
        }

        if (isComplex && outputImageInComplex) {
          sigma = dn * Math.sqrt(calibrationFactor);
        } else {
          sigma = dn2 * calibrationFactor;
        }

        if (outputImageScaleInDb) { // convert calibration result to dB
          if (sigma < underFlowFloat) {
            sigma = -underFlowFloat;
          } else {
            sigma = 10.0 * Math.log10(sigma);
          }
        }

        trgData.setElemDoubleAt(tgtIdx, sigma);
      }
    }
  }