/*
 * Copyright 2008 ZXing authors
 *
 * 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.
 */

using System;
using System.Collections.Generic;
using System.Text;

using ZXing.Common;

namespace ZXing.OneD
{
    /// <summary>
    ///   <p>Implements decoding of the UPC-A format.</p>
    ///   <author>dswitkin@google.com (Daniel Switkin)</author>
    ///   <author>Sean Owen</author>
    /// </summary>
    public sealed class UPCAReader : UPCEANReader
    {
        private readonly UPCEANReader ean13Reader = new EAN13Reader();

        /// <summary>
        ///   <p>Like decodeRow(int, BitArray, java.util.Map), but
        /// allows caller to inform method about where the UPC/EAN start pattern is
        /// found. This allows this to be computed once and reused across many implementations.</p>
        /// </summary>
        /// <param name="rowNumber"></param>
        /// <param name="row"></param>
        /// <param name="startGuardRange"></param>
        /// <param name="hints"></param>
        /// <returns></returns>
        override public Result decodeRow(int rowNumber,
                                BitArray row,
                                int[] startGuardRange,
                                IDictionary<DecodeHintType, object> hints)
        {
            return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange, hints));
        }

        /// <summary>
        ///   <p>Attempts to decode a one-dimensional barcode format given a single row of
        /// an image.</p>
        /// </summary>
        /// <param name="rowNumber">row number from top of the row</param>
        /// <param name="row">the black/white pixel data of the row</param>
        /// <param name="hints">decode hints</param>
        /// <returns>
        ///   <see cref="Result"/>containing encoded string and start/end of barcode or null, if an error occurs or barcode cannot be found
        /// </returns>
        override public Result decodeRow(int rowNumber, BitArray row, IDictionary<DecodeHintType, object> hints)
        {
            return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, hints));
        }

        /// <summary>
        /// Decodes the specified image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="hints">The hints.</param>
        /// <returns></returns>
        override public Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints)
        {
            return maybeReturnResult(ean13Reader.decode(image, hints));
        }

        /// <summary>
        /// Get the format of this decoder.
        /// <returns>The 1D format.</returns>
        /// </summary>
        internal override BarcodeFormat BarcodeFormat
        {
            get { return BarcodeFormat.UPC_A; }
        }

        /// <summary>
        /// Subclasses override this to decode the portion of a barcode between the start
        /// and end guard patterns.
        /// </summary>
        /// <param name="row">row of black/white values to search</param>
        /// <param name="startRange">start/end offset of start guard pattern</param>
        /// <param name="resultString"><see cref="StringBuilder"/>to append decoded chars to</param>
        /// <returns>
        /// horizontal offset of first pixel after the "middle" that was decoded or -1 if decoding could not complete successfully
        /// </returns>
        protected internal override int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString)
        {
            return ean13Reader.decodeMiddle(row, startRange, resultString);
        }

        private static Result maybeReturnResult(Result result)
        {
            if (result == null)
                return null;

            var text = result.Text;
            if (text[0] == '0')
            {
                var upcaResult = new Result(text.Substring(1), null, result.ResultPoints, BarcodeFormat.UPC_A);
                if (result.ResultMetadata != null)
                {
                    upcaResult.putAllMetadata(result.ResultMetadata);
                }
                return upcaResult;
            }
            else
            {
                return null;
            }
        }
    }
}