<?php
/**
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Google\ApiCore\Testing;

use Google\Protobuf\Internal\Message;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Exporter\Exporter;

/**
 * @internal
 */
class ProtobufMessageComparator extends Comparator
{
    /** @var Exporter */
    protected $exporter;

    public function __construct()
    {
        parent::__construct();
        $this->exporter = new MessageAwareExporter();
    }

    /**
     * Returns whether the comparator can compare two values.
     *
     * @param mixed $expected The first value to compare
     * @param mixed $actual The second value to compare
     * @return boolean
     */
    public function accepts($expected, $actual)
    {
        return $expected instanceof Message && $actual instanceof Message;
    }

    /**
     * Asserts that two values are equal.
     *
     * @param Message $expected The first value to compare
     * @param Message $actual The second value to compare
     * @param float|int $delta The allowed numerical distance between two values to
     *                      consider them equal
     * @param  bool $canonicalize If set to TRUE, arrays are sorted before
     *                             comparison
     * @param  bool $ignoreCase If set to TRUE, upper- and lowercasing is
     *                           ignored when comparing string values
     * @throws ComparisonFailure Thrown when the comparison
     *                           fails. Contains information about the
     *                           specific errors that lead to the failure.
     */
    public function assertEquals($expected, $actual, $delta = 0, $canonicalize = false, $ignoreCase = false)
    {
        if (get_class($expected) === get_class($actual)) {
            if ($expected->serializeToString() === $actual->serializeToString()) {
                return;
            }

            try {
                if (json_decode($expected->serializeToJsonString(), true) == json_decode($actual->serializeToJsonString(), true)) {
                    return;
                }
            } catch (\Throwable $e) {
                // Fall through to throw ComparisonFailure
            }
        }

        throw new ComparisonFailure(
            $expected,
            $actual,
            $this->exporter->shortenedExport($expected),
            $this->exporter->shortenedExport($actual),
            false,
            'Given 2 Message objects are not the same'
        );
    }
}
