private void GenerateAlbumWiseData(Album album, ingestion i, String[] files)
        {
            if (album == null || i == null)
            {
                return;
            }

            // Action
            try
            {
                if (album.ActionTypeValue != null)
                {
                    i.action = _actionConverter[(Album.ActionType)album.ActionTypeValue];
                }
            }
            catch (Exception ex)
            {
                Notify(String.Format("Album [{0}]: Invalid action.", album.CatalogReference));
                Debug.WriteLine(this, "FugaXmlCatalogWriter.GenerateAlbumWiseData, exception=" + ex.Message);
            }

            i.album.additional_artists = null;

            i.album.album_notes = null;

            // TODO i.album.alternate_genre

            i.album.alternate_genreSpecified = false;

            // TODO i.album.alternate_subgenre

            var attachmentFile = SearchFilename(files, FugaIngestionFileType.Attachment);
            if (!String.IsNullOrEmpty(attachmentFile))
            {
                var f = new FileInfo(attachmentFile);
                i.album.attachments = new List<attachment_type>
                {
                    new attachment_type
                    {
                        name = "Booklet",
                        description = "Booklet",
                        file = new file_type
                        {
                            name = attachmentFile.GetFileNameFromFullPath(),
                            size = (ulong) f.Length,
                        },
                    },
                };
            }
            else
            {
                i.album.attachments = null;
            }

            i.album.c_line_text = (!String.IsNullOrEmpty(album.CName)) ? album.CName.Typo() : CatalogContext.Instance.Settings.COwnerDefault.Typo();

            i.album.c_line_year = album.CYear != null ? album.CYear.ToString() : null;

            i.album.catalog_number = album.CatalogReference.Typo();

            i.album.catalog_tier = _albumTierConverter[album.Tier];

            i.album.catalog_tierSpecified = true;

            i.album.consumer_release_date = album.ConsumerReleaseDate;

            var coverFile = SearchFilename(files, FugaIngestionFileType.Cover);
            if (!String.IsNullOrEmpty(coverFile))
            {
                var f = new FileInfo(coverFile);
                i.album.cover_art = new ingestionAlbumCover_art
                {
                    image = new ingestionAlbumCover_artImage
                    {
                        file = new file_type
                        {
                            name = coverFile.GetFileNameFromFullPath(),
                            size = (ulong)f.Length,
                        },
                    },
                };
            }

            // TODO i.album.display_artist
            // TODO i.album.extra1
            // TODO i.album.extra2
            // TODO i.album.extra3
            // TODO i.album.extra4
            // TODO i.album.extra5
            // TODO i.album.extra6
            // TODO i.album.extra7
            // TODO i.album.extra8
            // TODO i.album.extra9
            // TODO i.album.extra9Specified
            // TODO i.album.extra10
            // TODO i.album.extra10Specified
            // TODO i.album.fuga_id

            i.album.label = album.Owner.Typo();

            i.album.language = ingestionAlbumLanguage.EN; // TODO

            i.album.languageSpecified = true;

            i.album.main_genre = genre_type.Classical; // TODO add a converter

            if (album.Subgenre != null && !String.IsNullOrEmpty(album.Subgenre.Name))
            {
                i.album.main_subgenre = album.Subgenre.Name.Typo();
            }
            else
            {
                Notify(String.Format("Album [{0}]: Missing subgenre.", album.CatalogReference));
            }

            if (album.Title != null && album.Title.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName))
            {
                i.album.name = album.Title[CatalogContext.Instance.DefaultLang.ShortName].Typo();
            }

            // TODO i.album.original_release_date
            // TODO i.album.original_release_dateSpecified

            i.album.p_line_text = (!String.IsNullOrEmpty(album.PName)) ? album.PName.Typo() : CatalogContext.Instance.Settings.POwnerDefault.Typo();

            i.album.p_line_year = album.PYear != null ? album.PYear.ToString() : null;

            i.album.parental_advisory = parental_advisory.@false; // TODO add setting

            i.album.parental_advisorySpecified = true;

            i.album.pricing_intervals = null;

            i.album.pricings = null;

            if (album.PrimaryArtistId > 0)
            {
                var primaryArtist = CatalogContext.Instance.Artists.FirstOrDefault(e => e.Id.Equals(album.PrimaryArtistId));
                if (primaryArtist != null && primaryArtist.LastName.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName))
                {
                    i.album.primary_artist = new primary_artist
                    {
                        name = (primaryArtist.FirstName[CatalogContext.Instance.DefaultLang.ShortName].Typo() + " " + primaryArtist.LastName[CatalogContext.Instance.DefaultLang.ShortName].Typo()).Trim(),
                    };
                }
            }

            i.album.recording_location =
                (
                    String.IsNullOrEmpty(album.RecordingLocation)
                    && FugaXmlConfig.DefaultRecordingLocationLabel != null
                    && FugaXmlConfig.DefaultRecordingLocationLabel.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                )
                ? FugaXmlConfig.DefaultRecordingLocationLabel[CatalogContext.Instance.DefaultLang.ShortName]
                : album.RecordingLocation.Typo();

            i.album.recording_year = album.RecordingYear != null ? album.RecordingYear.ToString() : null;

            i.album.redeliveries = new redeliveries_type
            {
                redeliver = false,
                only_to_organizations = null,
            };

            i.album.release_format_type = ingestionAlbumRelease_format_type.ALBUM; // TODO translater

            // TODO i.album.release_version
            // TODO i.album.schedule

            i.album.supplier = CatalogContext.Instance.Settings.SupplierDefault.Typo();

            i.album.territories = new List<territory_code>
            {
                territory_code.WORLD,
            };

            if (album.TotalDiscs != null)
            {
                i.album.total_discs = album.TotalDiscs.ToString();
            }
            else
            {
                Notify(String.Format("Album [{0}]: Missing total discs.", album.CatalogReference));
            }

            if (album.Ean != null)
            {
                i.album.upc_code = album.Ean.ToString();
            }
            else
            {
                Notify(String.Format("Album [{0}]: Missing UPC/EAN.", album.CatalogReference));
            }

            i.album.usage_rights = null;
        }
        private void GenerateTrackWiseData(Album album, KeyValuePair<short, Dictionary<short, String>> volume, ingestion i, String[] files)
        {
            if (album == null || i == null)
            {
                return;
            }
            var volumeIndex = volume.Key;
            var volumeTracks = volume.Value;

            // Artists buffer for performance improvement
            var artistsBuffer = new List<Artist>();

            foreach (var volumeTrack in volumeTracks)
            {
                var trackIndex = volumeTrack.Key;
                var assetId = volumeTrack.Value;
                var asset = CatalogContext.Instance.Assets.FirstOrDefault(e => String.Compare(e.Id, assetId, StringComparison.Ordinal) == 0);
                if (asset == null)
                {
                    Notify(String.Format("Album [{0}] [{1},{2}]: Asset key reference not found.", album.CatalogReference, volumeIndex, trackIndex));
                    continue;
                }

                // The asset's work is either standalone or a child work.
                var currentWork = CatalogContext.Instance.Works.FirstOrDefault(e => e.Id.Equals(asset.Work));
                if (currentWork == null)
                {
                    Notify(String.Format("Album [{0}] [{1},{2}]: Work not found for asset {3}.", album.CatalogReference, volumeIndex, trackIndex, asset.Id));
                    continue;
                }
                var parentWork = (currentWork.Parent > 0)
                    ? CatalogContext.Instance.Works.FirstOrDefault(w => w.Id.Equals(currentWork.Parent))
                    : null;
                var assetPerformersKeys = asset.Contributors.Keys.ToArray();
                if (assetPerformersKeys == null || !(assetPerformersKeys.Length > 0))
                {
                    Notify(String.Format("Album [{0}] [{1},{2}]: No performers.", album.CatalogReference, volumeIndex, trackIndex));
                    continue;
                }

                var ingestionTrack = new ingestionAlbumTracksClassical_track();

                // Additional artists, if any, are from the second performer.
                if (assetPerformersKeys.Length > 1)
                {
                    ingestionTrack.additional_artists = new List<artist>();

                    for (var j = 1; j < assetPerformersKeys.Length; j++)
                    {
                        var additionalArtist = CatalogContext.Instance.Artists.FirstOrDefault(e =>
                            e.Id.Equals(assetPerformersKeys[j]));
                        if (additionalArtist.LastName.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName))
                        {
                            ingestionTrack.additional_artists.Add(new artist
                            {
                                name = (additionalArtist.FirstName[CatalogContext.Instance.DefaultLang.ShortName].Typo() + " " + additionalArtist.LastName[CatalogContext.Instance.DefaultLang.ShortName].Typo()).Trim(),
                                primary_artist = true, // TODO manage primary character for additional artists
                            });
                        }
                    }
                }
                else
                {
                    ingestionTrack.additional_artists = null;
                }

                ingestionTrack.allow_preorder_preview = false;

                ingestionTrack.allow_preorder_previewSpecified = true;

                // TODO asset.alternate_genre

                ingestionTrack.alternate_genreSpecified = false;

                // TODO asset.alternate_subgenre

                ingestionTrack.always_send_display_title = true;

                ingestionTrack.always_send_display_titleSpecified = true;

                ingestionTrack.available_separately = asset.AvailableSeparately;

                ingestionTrack.catalog_tier = _isrcTierConverter[(CatalogTier)asset.Tier];

                ingestionTrack.catalog_tierSpecified = true;

                ingestionTrack.classical_catalog = (parentWork == null) ? currentWork.ClassicalCatalog : parentWork.ClassicalCatalog;

                if (currentWork.Contributors != null && currentWork.Contributors.Count > 0)
                {
                    ingestionTrack.contributors = new List<contributor>(); // These represent work-related contributors like typically Composer, Arranger, etc.
                    foreach (var workContributor in currentWork.Contributors)
                    {
                        Artist artist;
                        if (artistsBuffer.Exists(a => a.Id.Equals(workContributor.Key)))
                        {
                            // Present in buffer
                            artist = artistsBuffer.FirstOrDefault(a => a.Id.Equals(workContributor.Key));
                        }
                        else
                        {
                            // Find then add to buffer
                            artist = CatalogContext.Instance.Artists.FirstOrDefault(a => a.Id.Equals(workContributor.Key));
                            artistsBuffer.Add(artist);
                        }
                        var role = CatalogContext.Instance.Roles.FirstOrDefault(r => String.Compare(r.Name, workContributor.Value, StringComparison.Ordinal) == 0);
                        var cRole = (_roleConverter.ContainsKey(role.Reference))
                            ? _roleConverter[role.Reference]
                            : contributorRole.ContributingArtist;

                        if (artist.LastName.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName))
                        {
                            ingestionTrack.contributors.Add(new contributor
                            {
                                name = (artist.FirstName[CatalogContext.Instance.DefaultLang.ShortName].Typo() + " " + artist.LastName[CatalogContext.Instance.DefaultLang.ShortName].Typo()).Trim(),
                                role = cRole,
                            });
                        }
                    }
                }
                else
                {
                    ingestionTrack.contributors = null;
                }

                // TODO asset.country_of_commissioning
                // TODO asset.country_of_recording

                if  (
                        currentWork.Title.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                        &&  (
                                parentWork == null
                                || parentWork.Title.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                            )
                    )
                {
                    ingestionTrack.display_title = (parentWork == null)
                        ? currentWork.Title[CatalogContext.Instance.DefaultLang.ShortName].Typo()
                        : parentWork.Title[CatalogContext.Instance.DefaultLang.ShortName].Typo() + StringHelper.HierarchicalSeparator(CatalogContext.Instance.DefaultLang) + currentWork.Title[CatalogContext.Instance.DefaultLang.ShortName].Typo();
                }

                // TODO asset.extra1
                // TODO asset.extra2
                // TODO asset.extra3
                // TODO asset.extra4
                // TODO asset.extra5
                // TODO asset.extra6
                // TODO asset.extra7
                // TODO asset.extra8
                // TODO asset.extra9
                // TODO asset.extra9Specified
                // TODO asset.extra10
                // TODO asset.extra10Specified

                ingestionTrack.isrc_code = assetId.ToFugaFormattedIsrc();

                ingestionTrack.keySpecified = (parentWork == null && currentWork.Tonality != null) || (parentWork != null && parentWork.Tonality != null);

                if (ingestionTrack.keySpecified)
                {
                    ingestionTrack.key = (parentWork == null)
                        ? _keyConverter[(Key)(currentWork.Tonality)]
                        : _keyConverter[(Key)(parentWork.Tonality)];
                }

                ingestionTrack.lyrics = String.Empty;

                if (album.Subgenre != null)
                {
                    ingestionTrack.main_subgenre = album.Subgenre.Name.Typo(); // TODO implement trackwise field
                }

                if  (
                        parentWork != null
                        && currentWork.MovementTitle.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                        && !String.IsNullOrEmpty(currentWork.MovementTitle[CatalogContext.Instance.DefaultLang.ShortName])
                    )
                {
                    ingestionTrack.movement = currentWork.MovementTitle[CatalogContext.Instance.DefaultLang.ShortName].Typo();
                }

                if (parentWork != null && currentWork.MovementNumber > 0)
                {
                    ingestionTrack.movement_number = currentWork.MovementNumber.ToString();
                }

                ingestionTrack.on_disc = volumeIndex.ToString(CultureInfo.InvariantCulture);

                ingestionTrack.p_line_text = asset.PName.Typo();

                ingestionTrack.p_line_year = asset.PYear != null ? asset.PYear.ToString() : null;

                ingestionTrack.parental_advisory = parental_advisory.@false; // TODO add setting

                // TODO asset.preorder_type

                ingestionTrack.preorder_typeSpecified = false;

                ingestionTrack.preview_length = "30"; // TODO add setting

                ingestionTrack.preview_start = "0";

                // Primary artists is performer 1 (asset contributor 1)
                var primaryArtist = CatalogContext.Instance.Artists.FirstOrDefault(e =>
                    e.Id.Equals(assetPerformersKeys[0]));
                if  (
                        primaryArtist != null
                        && primaryArtist.LastName != null
                        && primaryArtist.LastName.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                    )
                {
                    ingestionTrack.primary_artist = new primary_artist
                    {
                        name = (primaryArtist.FirstName[CatalogContext.Instance.DefaultLang.ShortName].Typo() + " " + primaryArtist.LastName[CatalogContext.Instance.DefaultLang.ShortName].Typo()).Trim(),
                    };
                }

                // TODO asset.publishers

                ingestionTrack.recording_location =
                    (
                        String.IsNullOrEmpty(asset.RecordingLocation)
                        && FugaXmlConfig.DefaultRecordingLocationLabel != null
                        && FugaXmlConfig.DefaultRecordingLocationLabel.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                    )
                    ? FugaXmlConfig.DefaultRecordingLocationLabel[CatalogContext.Instance.DefaultLang.ShortName]
                    : asset.RecordingLocation.Typo();

                ingestionTrack.recording_year = asset.RecordingYear != null ? asset.RecordingYear.ToString() : null;

                // TODO asset.redeliveries_of_associated
                var audioFilename = SearchFilename(files, FugaIngestionFileType.AudioTrack, new KeyValuePair<int, int>(volumeIndex, trackIndex));
                if (!String.IsNullOrEmpty(audioFilename))
                {
                    var f = new FileInfo(audioFilename);
                    ingestionTrack.resources = new List<resourcesAudio>
                    {
                        new resourcesAudio
                        {
                            file = new file_type
                            {
                                name = audioFilename.GetFileNameFromFullPath(),
                                size = (ulong) f.Length,
                            }
                        },
                    };
                }
                else
                {
                    ingestionTrack.resources = null;
                }

                // TODO asset.rights_contract_begin_date
                // TODO asset.rights_contract_begin_dateSpecified
                // TODO asset.rights_holder_name
                // TODO asset.rights_ownership_name

                ingestionTrack.sequence_number = trackIndex.ToString(CultureInfo.InvariantCulture);

                ingestionTrack.track_notes = String.Empty;

                ingestionTrack.track_version = String.Empty;

                ingestionTrack.usage_rights = null;

                ingestionTrack.work = new work
                {
                    name = parentWork != null
                           && parentWork.Title != null
                           && parentWork.Title.ContainsKey(CatalogContext.Instance.DefaultLang.ShortName)
                        ? parentWork.Title[CatalogContext.Instance.DefaultLang.ShortName].Typo()
                        : String.Empty,
                };

                // Add asset
                i.album.tracks.Items.Add(ingestionTrack);
            }
        }
        /// <summary>
        /// Albums parser
        /// </summary>
        private async Task ParseAlbums()
        {
            Notify("Parsing albums.");
            if (_albums == null)
            {
                return;
            }

            foreach (var row in _albums)
            {
                Dictionary<int, object> cells = null;
                var rowCopy = row;
                await Task.Run(() =>
                {
                    cells = CellMapByRow(rowCopy, AlbumWorksheetName);
                });

                if (cells != null && cells.Count > 0)
                {
                    {
                        var genreTag = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["genre"]);
                        var subgenreTag = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["subgenre"]);
                        Album.ActionType? actionTypeValue = null;
                        switch (CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["action"]).ToLower())
                        {
                            case "insert":
                                actionTypeValue = Album.ActionType.Insert;
                                break;
                            case "update":
                                actionTypeValue = Album.ActionType.Update;
                                break;
                        }
                        var album = new Album
                        {
                            Id = Convert.ToInt32(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["album_id"], "0")),
                            ActionTypeValue = actionTypeValue,
                            CName = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["c_name"]),
                            CYear = Convert.ToInt16(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["c_year"], "0")),
                            PName = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["p_name"]),
                            PYear = Convert.ToInt16(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["p_year"], "0")),
                            RecordingLocation = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["recording_location"]),
                            RecordingYear = Convert.ToInt16(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["recording_year"], "0")),
                            Owner = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["label"]),
                            CatalogReference = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["reference"]),
                            Ean = Convert.ToInt64(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["ean"], "0")),
                            ConsumerReleaseDate = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["consumer_release_date"]).ToDateTime(),
                            Title = new Dictionary<String, String>
                        {
                            {CatalogContext.Instance.DefaultLang.ShortName, CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["title_" + CatalogContext.Instance.DefaultLang.ShortName])},
                        },
                            Genre = (CatalogContext.Instance.Tags.Exists(t => String.Compare(t.Name, genreTag, StringComparison.Ordinal) == 0))
                                ? CatalogContext.Instance.Tags.FirstOrDefault(t => String.Compare(t.Name, genreTag, StringComparison.Ordinal) == 0)
                                : null,
                            Subgenre = (!String.IsNullOrEmpty(subgenreTag) && CatalogContext.Instance.Tags.Exists(t => String.Compare(t.Name, subgenreTag, StringComparison.Ordinal) == 0))
                                ? CatalogContext.Instance.Tags.FirstOrDefault(t => String.Compare(t.Name, subgenreTag, StringComparison.Ordinal) == 0)
                                : null,
                            PrimaryArtistId = Convert.ToInt32(CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["primary_artist"], "0")),
                            Redeliver = false,
                        };

                        if (album == null || album.Id <= 0)
                        {
                            if (_mainFormViewModel != null)
                            {
                                _mainFormViewModel.InputProgressBarValue++;
                            }
                            continue;
                        }

                        if (album.ActionTypeValue == null)
                        {
                            album.ActionTypeValue = Album.ActionType.Insert;
                        }

                        if (String.IsNullOrEmpty(album.CName))
                        {
                            album.CName = CatalogContext.Instance.Settings.LabelDefault;
                        }

                        if (album.CYear < BabelMetaConfig.RecordingYearLowerBound || album.CYear > BabelMetaConfig.RecordingYearUpperBound)
                        {
                            album.CYear = null;
                        }

                        if (String.IsNullOrEmpty(album.PName))
                        {
                            album.PName = CatalogContext.Instance.Settings.LabelDefault;
                        }

                        if (album.PYear < BabelMetaConfig.RecordingYearLowerBound || album.PYear > BabelMetaConfig.RecordingYearUpperBound)
                        {
                            album.PYear = null;
                        }

                        if (album.RecordingYear < BabelMetaConfig.RecordingYearLowerBound || album.RecordingYear > BabelMetaConfig.RecordingYearUpperBound)
                        {
                            album.RecordingYear = null;
                        }

                        if (String.IsNullOrEmpty(album.Owner))
                        {
                            album.Owner = CatalogContext.Instance.Settings.LabelDefault;
                        }

                        var tier = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["catalog_tier"]).ToLower();
                        if (!String.IsNullOrEmpty(tier))
                        {
                            switch (tier)
                            {
                                case "back": album.Tier = CatalogTier.Back; break;
                                case "budget": album.Tier = CatalogTier.Budget; break;
                                case "front": album.Tier = CatalogTier.Front; break;
                                case "mid": album.Tier = CatalogTier.Mid; break;
                                case "premium": album.Tier = CatalogTier.Premium; break;
                            }
                        }
                        else
                        {
                            album.Tier = CatalogContext.Instance.Settings.CatalogTierDefault;
                        }

                        if (album.Ean != null && album.Ean < 0) { album.Ean = null; }


                        // Object may have several lang-dependent field sets
                        var otherLangs = CatalogContext.Instance.Langs.Where(l => l != CatalogContext.Instance.DefaultLang).ToList();
                        foreach (var lang in otherLangs)
                        {
                            var title = CellContentWizard(cells, _worksheetColumns[AlbumWorksheetName]["title_" + lang.ShortName]);
                            album.Title[lang.ShortName] = (!String.IsNullOrEmpty(title)) ? album.Title[CatalogContext.Instance.DefaultLang.ShortName] : title;
                        }

                        CatalogContext.Instance.Albums.Add(album);
                    }
                }

                if (_mainFormViewModel != null)
                {
                    _mainFormViewModel.InputProgressBarValue++;
                }
            }
        }