// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License

using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.Bindings;
using UnityEngine.Scripting;
using UnityEngine.Scripting.APIUpdating;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
using UnityEditor.Experimental;

namespace UnityEditor.AssetImporters
{
    /// Universal structure that holds all the data relevant to importing an asset, including temporary data that needs to be shared across stages that make on any given importer's pipeline.
    ///
    /// Breaking up legacy importers into peaces and re-arranging them as pipelines that use those pieces so that the pieces can become building blocks that other importers can re-use implies that the pieces
    /// is not coupled with any given importer. For this decoupling and maximizing reuse, we need something that can hold the information describing what is being imported but also the data generated by the
    /// various parts that make up an importers pipeline. This container simply transports information from one "stage" to the other. Each stage is free to add/delete/alter the content of the container
    [RequiredByNativeCode]
    [NativeHeader("Editor/Src/AssetPipeline/AssetImportContext.h")]
    [MovedFrom("UnityEditor.Experimental.AssetImporters")]
    public class AssetImportContext
    {
        // The bindings generator is setting the instance pointer in this field
        internal IntPtr m_Self;

        // the context can only be instantiated in native code
        AssetImportContext() {}

        public extern string assetPath { get; internal set; }

        [Obsolete("GetResultPath has been deprecated. Use GetOutputArtifactFilePath(string) instead (UnityUpgradable) -> GetOutputArtifactFilePath(*)")]
        [NativeName("GetOutputArtifactFilePath")]
        public extern string GetResultPath(string extension);

        public extern BuildTarget selectedBuildTarget { get; }

        [NativeThrows]
        public extern void SetMainObject(Object obj);
        public extern Object mainObject { get; }

        public void AddObjectToAsset(string identifier, Object obj)
        {
            AddObjectToAsset(identifier, obj, null);
        }

        [FreeFunction("AssetImportContextBindings::GetObjects", HasExplicitThis = true)]
        public extern void GetObjects([NotNull] List<Object> objects);

        [NativeThrows]
        public extern void AddObjectToAsset(string identifier, Object obj, Texture2D thumbnail);

        // Create a dependency against the contents of the source asset at the provided path
        // * if the asset at the path changes, it will trigger an import
        // * if the asset at the path moves, it will trigger an import
        public void DependsOnSourceAsset(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path", "Cannot add dependency on invalid path.");
            }

            DependsOnSourceAssetInternal(path);
        }

        [NativeName("DependsOnSourceAsset")]
        private extern void DependsOnSourceAssetInternal(string path);

        public void DependsOnSourceAsset(GUID guid)
        {
            if (guid.Empty())
            {
                throw new ArgumentNullException("guid", "Cannot add dependency on empty GUID.");
            }

            DependsOnSourceAssetInternalGUID(guid);
        }

        [NativeName("DependsOnSourceAsset")]
        private extern void DependsOnSourceAssetInternalGUID(GUID guid);

        [NativeName("GetFolderEntries")]
        internal extern GUID[] GetFolderEntries(GUID folder);

        internal void DependsOnImportedAsset(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path", "Cannot add dependency on invalid path.");
            }

            DependsOnImportedAssetInternal(path);
        }

        [NativeName("GetArtifactFilePath")]
        private extern string GetArtifactFilePath_Internal(string path, string fileName);

        public string GetArtifactFilePath(string path, string fileName)
        {
            return GetArtifactFilePath_Internal(path, fileName);
        }

        public string GetArtifactFilePath(GUID guid, string fileName)
        {
            return GetArtifactFilePath(new ArtifactKey(guid), fileName);
        }

        public extern string GetArtifactFilePath(ArtifactKey key, string fileName);

        public extern string GetOutputArtifactFilePath(string fileName);

        [NativeName("DependsOnImportedAsset")]
        private extern void DependsOnImportedAssetInternal(string path);

        public void DependsOnArtifact(ArtifactKey key)
        {
            if (!key.isValid)
            {
                throw new ArgumentNullException("key", "Cannot add dependency on invalid ArtifactKey.");
            }

            DependsOnArtifactInternal(key);
        }

        [NativeName("DependsOnArtifact")]
        private extern void DependsOnArtifactInternal(ArtifactKey key);

        public void DependsOnArtifact(GUID guid)
        {
            if (guid.Empty())
            {
                throw new ArgumentNullException("guid", "Cannot add dependency on empty GUID.");
            }

            DependsOnArtifactInternalGUID(guid);
        }

        [NativeName("DependsOnArtifact")]
        private extern void DependsOnArtifactInternalGUID(GUID guid);

        public void DependsOnArtifact(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path", "Cannot add dependency on invalid path.");
            }

            DependsOnArtifactInternalPath(path);
        }

        [NativeName("DependsOnArtifact")]
        private extern void DependsOnArtifactInternalPath(string path);

        public void DependsOnCustomDependency(string dependency)
        {
            if (string.IsNullOrEmpty(dependency))
            {
                throw new ArgumentNullException("dependency", "Cannot add custom dependency on an empty custom dependency.");
            }

            if (string.CompareOrdinal(dependency,"srp/default-shader") == 0 && assetPath.EndsWith(".shader", StringComparison.OrdinalIgnoreCase))
            {
                throw new Exception($"A shader '{assetPath}' cannot depend on the 'srp/default-shader' custom dependency because this operation is unsupported.");
            }

            DependsOnCustomDependencyInternal(dependency);
        }

        [NativeName("DependsOnCustomDependency")]
        private extern void DependsOnCustomDependencyInternal(string path);

        extern void AddImportLog(string msg, string file, int line, ImportLogFlags flags, UnityEngine.Object obj);

        void AddImportLog(string msg, ImportLogFlags flags, UnityEngine.Object obj)
        {
            var st = new StackTrace(2, true);
            var sf = st.GetFrame(0);
            AddImportLog(msg, sf.GetFileName(), sf.GetFileLineNumber(), flags, obj);
        }

        public void LogImportError(string msg, UnityEngine.Object obj = null)
        {
            AddImportLog(msg, ImportLogFlags.Error, obj);
        }

        internal void LogImportError(string msg, string file, int line, UnityEngine.Object obj = null)
        {
            AddImportLog(msg, file, line, ImportLogFlags.Error, obj);
        }

        public void LogImportWarning(string msg, UnityEngine.Object obj = null)
        {
            AddImportLog(msg, ImportLogFlags.Warning, obj);
        }

        internal void LogImportWarning(string msg, string file, int line, UnityEngine.Object obj = null)
        {
            AddImportLog(msg, file, line, ImportLogFlags.Warning, obj);
        }

    }
}
