package com.apobates.forum.thrones.rss;

import com.apobates.forum.utils.DateTimeUtils;
import com.apobates.forum.utils.DateTimeZoneEnum;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
import com.rometools.rome.feed.rss.Channel;
import com.rometools.rome.feed.rss.Content;
import com.rometools.rome.feed.rss.Item;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.stream.Collectors;
/**
 * RSS控制器视图
 *
 * @author xiaofanku
 * @since 20200511
 */
public class TopicRssView extends AbstractRssFeedView {
    public TopicRssView() {
        setContentType(MediaType.APPLICATION_XML_VALUE);
    }

    @Override
    protected List<Item> buildFeedItems(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        TopicRssResult data = (TopicRssResult) model.get("feeds");
        if(null == data){
            return Collections.emptyList();
        }
        return data.getItems().stream().map(tri->{
            Item item = new Item();
            item.setTitle(tri.getTitle());
            item.setLink(tri.getLink());
            item.setPubDate(toRomeDate(tri.getDate()));
            Content content = new Content();
            content.setValue(tri.getContent());
            item.setContent(content);
            return item;
        }).collect(Collectors.toList());
    }
    
    @Override
    protected void buildFeedMetadata(Map<String, Object> model, Channel feed, HttpServletRequest request) {
        TopicRssResult data = (TopicRssResult) model.get("feeds");
        if(null == data){
            super.buildFeedMetadata(model, feed, request);
            return;
        }
        feed.setTitle(data.getTitle());
        feed.setLink(data.getLink());
        feed.setDescription(data.getDesc());
        super.buildFeedMetadata(model, feed, request);
    }
    //只适用于祖国大陆
    private static Date toRomeDate(LocalDateTime localDateTime){
        String ds = DateTimeUtils.getRFC3339(localDateTime, DateTimeZoneEnum.CTT.toZone());
        return RomeDateUtils.parseW3CDateTime(ds, Locale.getDefault());
    }
    //以下代码出自:com.rometools.rome.io.impl.DateParser 
    private static class RomeDateUtils {
        // order is like this because the SimpleDateFormat.parse does not fail with exception if it can
        // parse a valid date out of a substring of the full string given the mask so we have to check
        // the most complete format first, then it fails with exception
        private final static String[] W3CDATETIME_MASKS = {"yyyy-MM-dd'T'HH:mm:ss.SSSz", "yyyy-MM-dd't'HH:mm:ss.SSSz", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
            "yyyy-MM-dd't'HH:mm:ss.SSS'z'", "yyyy-MM-dd'T'HH:mm:ssz", "yyyy-MM-dd't'HH:mm:ssz", "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd't'HH:mm:ssZ",
            "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd't'HH:mm:ss'z'", "yyyy-MM-dd'T'HH:mmz", // together
            // with
            // logic
            // in
            // the
            // parseW3CDateTime
            // they
            "yyyy-MM'T'HH:mmz", // handle W3C dates without time forcing them to
            // be GMT
            "yyyy'T'HH:mmz", "yyyy-MM-dd't'HH:mmz", "yyyy-MM-dd'T'HH:mm'Z'", "yyyy-MM-dd't'HH:mm'z'", "yyyy-MM-dd", "yyyy-MM", "yyyy"};
        
        /**
         * Parses a Date out of a String with a date in W3C date-time format.
         * <p/>
         * It parsers the following formats:
         * <ul>
         * <li>"yyyy-MM-dd'T'HH:mm:ssz"</li>
         * <li>"yyyy-MM-dd'T'HH:mmz"</li>
         * <li>"yyyy-MM-dd"</li>
         * <li>"yyyy-MM"</li>
         * <li>"yyyy"</li>
         * </ul>
         * <p/>
         * Refer to the java.text.SimpleDateFormat javadocs for details on the
         * format of each element.
         * <p/>
         *
         * @param sDate string to parse for a date.
         * @return the Date represented by the given W3C date-time string. It
         * returns <b>null</b> if it was not possible to parse the given string
         * into a Date.
         *
         */
        public static Date parseW3CDateTime(String sDate, final Locale locale) {
            // if sDate has time on it, it injects 'GTM' before de TZ displacement to allow the
            // SimpleDateFormat parser to parse it properly
            final int tIndex = sDate.indexOf("T");
            if (tIndex > -1) {
                if (sDate.endsWith("Z")) {
                    sDate = sDate.substring(0, sDate.length() - 1) + "+00:00";
                }
                int tzdIndex = sDate.indexOf("+", tIndex);
                if (tzdIndex == -1) {
                    tzdIndex = sDate.indexOf("-", tIndex);
                }
                if (tzdIndex > -1) {
                    String pre = sDate.substring(0, tzdIndex);
                    final int secFraction = pre.indexOf(",");
                    if (secFraction > -1) {
                        pre = pre.substring(0, secFraction);
                    }
                    final String post = sDate.substring(tzdIndex);
                    sDate = pre + "GMT" + post;
                }
            } else {
                sDate += "T00:00GMT";
            }
            return parseUsingMask(W3CDATETIME_MASKS, sDate, locale);
        }
        
        /**
         * Parses a Date out of a string using an array of masks.
         * <p/>
         * It uses the masks in order until one of them succedes or all fail.
         * <p/>
         *
         * @param masks array of masks to use for parsing the string
         * @param sDate string to parse for a date.
         * @return the Date represented by the given string using one of the
         * given masks. It returns
         * <b>null</b> if it was not possible to parse the the string with any
         * of the masks.
         *
         */
        private static Date parseUsingMask(final String[] masks, String sDate, final Locale locale) {
            if (sDate != null) {
                sDate = sDate.trim();
            }
            ParsePosition pp = null;
            Date d = null;
            for (int i = 0; d == null && i < masks.length; i++) {
                final DateFormat df = new SimpleDateFormat(masks[i].trim(), locale);
                // df.setLenient(false);
                df.setLenient(true);
                try {
                    pp = new ParsePosition(0);
                    d = df.parse(sDate, pp);
                    if (pp.getIndex() != sDate.length()) {
                        d = null;
                    }
                } catch (final Exception ex1) {}
            }
            return d;
        }
    }
}